Aria User Guide

Aria User Guide

Version 4.0
© Copyright Formaria Ltd., All rights reserved

Section I: Background

About this book

This user guide provides information on using and developing applications with Aria. The guide is organized into an number of sections.

  • Section I Begins with an introduction to Aria, its history and its position on the technology landscape.
  • Section II Describes the Aria Integrated Development Environment (IDE) and the basic features that are commonly included in an application are explained.
  • Section III Covers more advanced features and the features that are required to build full-featured client-server applications.
  • Section IV Illustrates many features by way of case studies.

The guide then concludes with a selection of reference materials.

Formaria also provides step-by-step tutorials, courseware and on-line reference manuals so the focus of this manual is on explaining the concepts of developing applications with Aria. The Formaria web site also features a developer zone where you can find working applications and numerous technical articles.

User Guide Conventions

The documentation for Aria uses the typefaces and symbols described in the table below to indicate special text.

TypefaceMeaning
Monospace typeMonospaced type represents text as it appears on screen or in Java code. It also represents anything you must type.
[] Square brackets in text or syntax listings enclose optional items. Do not type the brackets
<> Angle brackets in text or syntax listings indicate a variable string; type in a string appropriate for you code. Do not type the angle brackets. Angle brackets are also used for HTML tags.
...An ellipsis in syntax listing indicate code that is missing from the example.
Italics Italicized words represent Java identifiers, such as names of variables, classes, interfaces, components, properties, methods, and events. Italics are also used for new terms being defined
Keycaps This typeface indicates a key on your keyboard for example, “press ESC to exit”.

Contacting Formaria developer support

Formaria offers a variety of support options. These include free services on the Internet, where you can search our information base and connect with other users of Formaria products. in addition you can choose from several categories of telephone support, ranging from installation support to fee-based consultant-level support and detailed assistance.

For more information about Formaria’s developer support services, see our website at http://www.formaria.org.

When contacting support, please be prepared to give complete information about your environment, the version of the product you are using, and a detailed description of the problem.

Introduction

This is a draft update of the manual. The Aria project contains some recent changes that are not entirely reflected in this book. In particular classs names no longer start with an X, there is no `optional' package (just renamed) and the build process for the framework and the IDE plugins has changed and the description is yet to be updated in this book.

Aria is a Java and XML framework for creating applications with rich user interfaces.

Aria includes a plugin for the netBeans integrated development environment making the development and testing process as easy as possible without locking you into a proprietary solution. Aria also has a rich set of libraries, tools and wizards for rapid application development.

What is Aria?

First and foremost Aria is a framework for building Java and XML based applications. The aims of Aria are to:

  • Reduce the amount of code needed to build an application
  • Provide a clean application architecture and infrastructure
  • Simplify building and maintaining applications
  • Allow easy extension and customization
  • Enable lightweight and performant applications

The core of Aria is a small Java library that allows 'pages' of an application to be constructed from an XML declaration or from Java code. Aria offers many add-ons and extensions including a graphical editor/IDE.

Aria can be used for simple single page applications and forms or for complex business applications comprising many page and resources.

What can be built with Aria?

Aria can be used to build a wide variety of applications just a few of which are listed below:

  • Simple form based applications
  • Management consoles
  • Catalogues, Configurators, Browsers
  • Selection tools
  • Guides/Brochures
  • Retail and Point-of-Sale applications
  • Kiosk applications
  • Mobile applications for field workers and road warriors
  • Surveys and market research applications
  • Database applications
  • e-learning applications
  • Informational and promotional applications

Aria is a general purpose tool so it can be used to build just about anything you can imagine building with Java and XML, but it offers the greatest advantage when building client side applications.

Aria also includes many sector specific features and add-ons. See the Xoetrope website for more information on add-ons and case studies highlighting how Aria's has been used to build specific solutions.

What does Aria do?

In a nutshell Aria can be used to build feature rich, smart-client applications. Smart client applications are typified by their ability to make use of client-side computing power. Notably this means that such applications exhibit;

  • A feature rich user interface
  • A fast and responsive user interface
  • Ability to work on-line or standalone
  • A simplified application construction
  • Ease of updated and processing of dynamic content
  • Built-in data management
  • Sophisticated functionality
  • Optimized bandwidth usage
  • Low maintenance and administration characteristics

All of these features can be found in Aria built applications. Furthermore, being Java and XML based, Aria applications are portable so that they can run on a wide variety of devices and platforms.

As a development platform Aria leverages the usual drag and drop techniques to make building applications quick and easy. Aria includes numerous widgets, library functions and wizards to make many common tasks simple.

Simplicity really is the key to Aria, so even if the feature or behavior you desire isn't predefined it usually isn't difficult to implement. As an open system Aria even allows such additions to be made in a reusable way so that you may only need to define the addition once. Even if you need to go beyond Aria's capabilities you can work directly with Java and XML or with third party tools. So, ultimately Aria make the developer's life easier.

How does Aria work?

Aria takes advantage of local computing resources, so instead of going back and forth to a server a Aria application can store and process information locally. As a result you don't see the delays that are typical of some web-based applications. Using the local computing resources means that you can make an application more responsive, offer more and better user interaction and build smarter applications, so-called Rich-Client or Rich Internet Applications (RIA).

Making use of local computing resources also helps simplify some of the tasks involved in building modern software applications. If you don't have to go back and forth to a server for information you don't need to do so much to maintain state information. Furthermore if you can eliminate input errors at source then your business processing should be less complicated.

In addition to the implicit benefits of a rich-client framework Aria also applies some well known architectural techniques to further simplify the process of building applications. The most important of these concepts is the Model-View-Controller (MVC) pattern. With this pattern the key components of an application are separated from one another to simplify development. In Aria the MVC architecture is promoted by the natural separation of the various components in framework and by the coding styles and techniques used for each component.

The Aria MVC components equate to

  • View: Pages defined in either XML or Java.
  • Model: The data, declaratively bound to the pages or view.
  • Controller: The application logic specified in Java and declaratively bound to the other components.

Aria also supports declarative user interface specification and declarative data binding whereby pages, data bindings and event bindings can be specified in XML independently of one another. The late coupling this provides has a number of benefits. Pages can be delivered or updated independently of one another much like HTML pages. This sort of independence of content was one of the major benefits of thin-client and web applications yet Aria manages to deliver similar behavior and benefits in a rich-client framework. Aria however achieves this while leveraging the Java platform, in the process delivering efficient, modular and reusable components.

Much of Aria's benefits stem from this loose coupling of components. Since the View is loosely couple to the data model a new look and feel can be placed on an application with minimal implications for the application behavior or coding. Similarly the application logic can be coded with minimal reference to the user interface or how data is bound and update to that user interface. Imagine being able to write the code for even a simple equation without having to know when or how the input fields are filled out or how to then display the results of your calculation.

The separation of roles pays dividends in a number of ways. People of differing skills can collaborate on a project with perhaps one concentrating on the user interface and visual side while another may be responsible for business logic or data processing. Whether this is actually a realizable business benefit is a moot point, but in the long-run the benefits are very real.

In a maintenance mode it may be many months since a particular feature or aspect of an application has been developed. Being able to `fix' a bug in say the business logic in such a feature is considerably easier when you don't risk breaking a user-interface feature or the data setup or vice versa. Furthermore without mixing user-interface code and business logic the role and intent of each piece of code is more apparent and this make understanding each part of the application a little less involved.

Aria is in some senses is a fairly typical development environment or framework for building Java applications, a plug-in for the NetBeans IDE is even a key feature. Aria however aims to make the process of building and maintaining applications as simple as possible.

Using the techniques described above Aria eliminates much of the normal `plumbing' code and allows you to get on with building real value into your application. In saving large amounts of coding the burden of developing and maintaining applications is also greatly lightened.

Licensing

Aria is available under the open source MPL and GPL/LGPL license which allows developers to download and use the software or to bundle it with commercial applications.

Any enquiries about licensing can be addressed to support@formaria.org.

Why Aria

There is no doubt that modern software is complex, and this complexity it often the bane of quality, performance and usability. Aria started out as a way of making life easier for the developer. The framework initially set out to eliminate some of the most basic and tedious coding tasks rather than as a complete replacement for existing practices. The stratgey of augmention rather than replacement has been followed throughout the frameworks's development and this leaves the developer free to use the best tools for a particular job, using all of Aria or just those parts that suit.

Over time Aria has developed to include a range of advanced features and tools that ease the task of building modern applications, but Aria's central theme remains the task of making it easier to build advanced, feature rich applications.

Background

Aria is a Java-based system that sets out to solve many of the problems encountered when building Rich Internet Applications (RIA).

Users are demanding more features, more performance, better integration and generally greater usability from their applications. A competitive market and demanding users means that modern applications are becoming more and more complex, often to the point where development is at the limit of the available resources and maintenance is highly expensive. For the many organizations this complexity may translate into less that ideal behavior or reduced functionality within the application.

While Aria is based upon Java, Java in its basic form can be misused, and to some extent Java suffers from some shortcomings from the application developer's perspective in that there is no out-of-the-box support is included for high-level abstractions such as the MVC architecture, and there is little high-level support for communication and data-access, and in some cases excessive plumbing is often required to perform simple tasks.

Aria aims to eliminate much of the plumbing involved in building RIAs, so that the developer can concentrate on adding value to the application.

Many of today's client applications, be they built on Java or not, suffer from code bloat and poor maintainability. Poor maintainability often derives from the use of IDEs and the throwaway nature of the code they generate. Unaddressed, this lack of emphasis on good design and quality leads to poor performance. For many, this poorly constructed application type is typified by the monolithic fat-client application, with all its deployment and maintenance issues.

One common solution to the problems of fat clients is to employ a thin-client architecture, moving processing to the server side and removing client administration woes, yet this solution is not without its own perils. Shifting the problem of poor design to the server won't win many awards and introduces many of its own problems, like security and session handling, not to mention performance.

Hybrid application types like Ajax share most of this thin-client legacy but added apparent interactivity, however, this comes add the price of even greater complexity and all without the architectural merits of either the fat or thin client application. Ajax applications also suffer from a lack of tooling, whereas a typical Java developer will have an abundance of superb tools.

Despite the poor reputation gained by some client applications, there has been a resurgence of interest in smart client applications. Most client devices, even handheld devices, now have sufficient processing capability to perform useful work and this is wasted in a thin-client environment. Many of the fat-client problems have been solved and the thin-client solution is not the panacea that many had desired.

The Aria library is an open-source framework designed to solve some of the problems involved in building modern smart client applications.

Why do we build applications at all?

It may seem obvious to say that we build application to meet business needs, yet it is worth reflecting on the bigger picture. What do we hope to accomplish with our applications?

Typical requirements include getting the work done in an efficient and effective manner and having a reliable and maintainable system. All quite straightforward, but what about keeping the customer happy? Quite often the job is not just about getting the immediate business done but also about keeping the customers happy.

Many of today's applications fall short of providing an all round satisfying user experience. Frequently the user simply wants to accomplish the task at hand with the least amount of pain. At other times the user wants a richer, more informative experience, one that adds value.

Aria can help you provide fast and effective client solutions.

The Form Metaphor

According to 2001 analyst estimates American businesses alone spend upward of $15 billion transferring data from paper-based forms such as loan applications and purchase orders to their computer systems. Billions more is invested every year in web based forms and other electronic forms. Not surprising when you consider the ubiquity of the humble form. Almost every business process involves form processing of one sort or another. In a broader view forms are an essential part of the data capture process for many businesses.

Electronic data capture and form processing offers many advantages over paper alternatives and act as the basis for a wide range of business applications. In many cases it may not even be possible to achieve comparable results with a paper based approach. Electronic forms can be of great value to both the end user (ease of use and error checking) and for backend processing (speed, quality and security).

Today a wide range of technologies are available for electronic forms processing. Many of the newer technologies are enabling completely new areas of automation and better, more functional applications. Couple this with other related technologies like mobile computing and wireless communication and you have many exciting new opportunities for improving workflow and communications.

From a business perspective automated form processing can bring many returns above and beyond the direct savings. The improved data can lead to better analysis of the customer relationship. Closing the enquiry-sales-fulfilment loop to manual data entry can even reduce leakage from the sales cycle. Where field workers or representatives are involved, faster more efficient technology will empower the employee.

In an application context the notion of a form can be quite vague but for the purposes of this guide we will assume that a form is a fairly discrete component that helps capture a subset of data. How that form is presented to the user can vary greatly within the definition of the user interface but from the point of view of the application logic the form's role is fairly well defined and helps for a logical subdivision of an application.

Beyond Forms

HTML forms have been the mainstay of web applications for some time. Although forms can be considered a workhorse of data capture, their use as the de facto user interface is not particularly well suited to the demands of modern business processes or for more complex application design. It is common for applications to comprise whole sets of forms and much of their value comes from relating the information on one form to another and to business intelligence.

It is easy to see that the smarter an application appears and the more responsive it is to the user, the richer the experience. Many of the web technologies in use today go to great lengths to provide such responsiveness and such intelligence, yet in many ways they face an uphill struggle. The infrastructure required to deploy some of these frameworks can be significant and rapidly pushes up the total cost of ownership despite what may be the obvious technical merits of an individual technology.

HTML was designed primarily for the display and linking of static text and static content. Extensions, plug-ins and many server side technologies have added to the power and capability of the basic HTML technology. The so called thin client that these technologies popularized has gained enormous popularity over the last few years. Despite the ubiquity and popularity of the thin client there are many areas where these web technologies have been less successful.

HTML's limits

An HTML application or form normally requires a request to the web server to provide updated content. Plug-ins or scripting may be used in some situations to avoid the round trip to the server and provide some element of local interactivity but often at the cost of complexity.

For anything other than simple applications the mix of HTML, JavaScript and server side technologies can rapidly lead to a complex technology solution. In many ways the solution is often more complex that the original problem. The more complex the applications become the greater the strain for these solutions to be effective.

Even without the implementation and delivery issues, HTML based applications suffer from the inherent delays associated with having to make frequent requests to the server. A simple round trip leads to noticeable delays in responding to user interaction and hence there is a limit to what is effective in terms of user interactivity with such applications.

Scripting

One of the defining characteristics of many web frameworks is the use of client-side scripting technologies. While scripting may be acceptable for small scale use it does not work well for large scale use. Scripting lacks many of the object-oriented features that programmers expect of a modern platform. Couple this to the common problem of separating roles with a environment that normally sees scripts embedded in user interface declarations and you can end up with a complex mess.

Underlying HTML's use is of course the browser, with all its security restrictions and compatibility problems. Even within an individual browser the lifecycle of a page can prove troublesome, making it difficult to maintain state. HTML is just not a natural fit for the needs of many modern applications.

Application delivery in a connected world

Today, a new range of connected workers has emerged, the road warrior, the business traveller, the salesman, the field worker, the PDA/Smart device owner. This type of user presents many new challenges. Typical of these demands is the occasionally connected nature of their usage. It's not much good to have smart devices if it is assumed that the application software needs constant connectivity, or if the applications cannot cope with loss of connectivity mid session.

Rich Client to the Rescue

Desktop computers and even the majority of mobile devices have significant computing power and therefore it is well within their capacity to handle much of what is delegated to the web server by thin client applications. One of the cornerstones of performance is locality of computing. Holding true to this maxim, locality of computing for a form application means that such things as data validation, prompting, user interaction can be handled locally. Doing things locally means not having to wait for a round trip to the server to complete. This alone can lead to faster, more interactive applications and interacting with data locally can also facilitate disconnected and mobile applications. Add to the local interactivity the rich user interfaces that can be built for today's rich client applications and you can get some very attractive applications.

So Why Aria?

Aria is a rich client application development platform designed to improve the way in which Forms and internet applications can be built. Bringing together two powerful technologies, Java and XML, Aria offers many advantages in terms of ease of development, performance and portability. These advantages lead directly to reduced development and maintenance costs. The rapid development process that Aria enables also means that you may have more time to devote to improving content and creativity.

The open source Aria framework addresses many of the problems discussed above and then some more. Aria adds many additional widgets for building applications and business sector specific features and wizards, not found in the basic open source platform. Aria also provides client-server support so that applications can more easily work within a corporate environment and communicate with back-end services.

Aria comes with a feature packed editor and is built in an open way, supporting open standards. Vendor lock-in is avoided and the editor can be used in conjunction with other tools. So even if your authors and developers don't want to use low level technologies like XML they can build applications with the familiar drag and drop metaphors.

The Development challenge

For many organizations the move to thin clients was driven by administrative issues as much as implementation ones. The thin client was seen as a way of reducing administrative and helpdesk requirements as the client application had zero footprint. Furthermore the assumed ease of updating content was considered a major benefit in itself.

The penalty for this approach was that more responsibility was placed on the server side. All the users, all the user information and preferences had to be managed by the server. All the processing was shifted to the server as well. While architecturally this structure may have seemed simpler it often involved several tiers and a large array of systems.

The return of the rich client will not do away with many of the demands placed on today's servers but it will certainly reduce the load and in many ways simplify the applications. With each client managing its own state the server can be focused on providing the centralized enterprise level facilities to which it is best suited.

A rich client framework like Aria implemented in a single language is considerably simpler than the highly layered approaches common in today's thin client systems. The problems that dogged rich clients of the past are but a memory. Zero footprint and incremental updates are a given. Security and a richer user experience are bonus.

Individual features of Aria are worth using on their own not to mention using them in concert with other Aria features. Take for example Aria's method of constructing user-interface components via factories. This feature alone saves large amounts of code over what would be generated by a typical IDE. Aria is a modular system so you can employ just one feature like this on its own, mixing and matching to your needs. Aria, can therefore be integrated into new, old or legacy applications without difficulty. For legacy applications the Aria framework can provide a convenient way of adding new and modern features to what may otherwise be tired looking applications.

Above all Aria is designed by programmers for programmers.

Related Technologies

Aria is built upon an open architecture and hence it can support a wide variety of technologies. Equally Aria can be used in an ad hoc manner whereby parts of the package can be used and other parts replaced or supplemented by other technologies. Aria was designed to support good programming practices and the use of best of breed tools so in many ways the technologies listed in this chapter need not be seen as competing technologies and can be used comfortably alongside Aria.

Aria versus HTML

HTML is well known and can be used for a wide variety of presentations and even for simple form applications. However HTML is not well suited to anything other than trivial applications. The mix of HTML with the scripts typically needed for interactive web pages can be a nightmare to develop and maintain. The scripting code tends to get heavily embedded in the HTML and it is therefore difficult to decipher and reuse.

Numerous attempts have been made to address the needs of web applications but many of these attempts require large plug-ins or are restricted to a Windows platform. We believe that Aria strikes a good balance, requiring no plug-ins, a small download requirement, pages that can be downloaded individually or en-masse all while still managing to have powerful programming capabilities.

The XML used by Aria is easy to read and understand. Separation of the scripting means that the XML is clean and compact; ideal for offloading to a graphic designer if so desired. The Java code used in place of the XML can run in the browser's native environment and is pure code, again no mixing of content so from the programmer's perspective this too is desirable.

Aria versus AJAX

AJAX isn't a new kid on the block, having been around in many guises for several years. AJAX is essentially a technique for updating a web page without having to request a page refresh. AJAX can update a fragment of the page and the end user has the impression of a more responsive environment as only some of the page updates. Indeed AJAX may be a little more efficient than vanilla HTML as the fragments of HTML/XML it pulls down during updates will conserve some bandwidth.

AJAX can enable some client side processing of data but it is at heart still a scripting technology, still burdened with all the drawbacks of scripting and of being browser based.

Aria versus Java

So why not program in Java? Aria's use of XML makes it compact and simplifies the process of creating forms and more generally simplifies the creation of a UI. The separation of concerns means that the core business logic can be separated from the UI with the consequence that the code is easier to develop and maintain. Not only that, but the code is also more likely to be reusable. In comparison to a Java solution developed with a typical integrate development environment we normally see a reduction of 50-70% in the size of the codebase.

Despite the benefits that Aria can bring we do not mean to suggest it can do everything. Java is a very powerful programming platform and Aria has been designed to work with Java and therefore the full power of Java is available to a Aria application.

Careful coding means that Aria can run with the browser's built in Java support (JDK 1.1.4 of Internet Explorer) and on a wide range of mobile devices. The result is that you aren't really faced with the choice of Java or Aria; rather you have the option of using the best features of both systems to give a powerful small footprint system.

Sun's Swing and NetBeans teams continue to make great strides in improving the Java desktop experience, however, most of the new technologies they introduce require a recent JDK and this may not be available to everyone. Most of Aria's technology is available on a wide range of JVMs.

Despite the overlap between Aria and some of the newer Swing features we plan to support emerging technologies as they mature and become available, particularly where they are standardized. Aria 3.0 introduces a new application architecture to make it easier to customize the basic application startup and behavior, and in this respect additional support for Swing has been added by way of a new startup object that can essentially integrate a Aria application as a component within a run-o-the-mill Swing application. Aria 3.0 also extends the Swing widget support, making use of third party Swing components very simple.

So what does all this mean? Well Aria and Aria play very well with Swing and standard Java desktop technologies. For the most part you can view Aria as a code saving technology for Swing, and after that you can use the normal Swing coding conventions.

Aria versus Servlet and Server Side Technologies

A number of server side technologies have been used to assist in the development of web applications. The Apache Foundation has produced several of these libraries, notably the Struts library. Sun, Microsoft, IBM, Oracle and others have added to the range of server side technologies available to the developer. The various technologies that these tools offer almost all produce HTML or XML that is rendered on the client. The server side tools add many facilities for validation of data and control of the client integration with the server but on the whole they fall short of offering the type of rich client that can be built with Aria and the client side technologies outlined above.

Of course most modern applications rely on a server side process to implement key business functions. Aria is no different and in this sense it is complementary to server side technologies. However Aria relieves the server of the challenges involved in much client state information.

Aria can integrate with business components on the backend via a variety of protocols and processes, be that simple HTML forms, J2EE, Soap or .Net. Aria's service model neatly encapsulates these services so that the much of the detail can be hidden from the application programmer.

Not only can you choose Aria and/or Java but using XML you can easily move from AWT to Swing, without recoding, so for a Windows user this means it is possible to run with Internet Explorer without need for additional downloads or plug-ins.

Support for other widget sets (e.g. LCDUI/Blackberry) is being developed from prototype).

Aria versus .NET

Microsoft's .Net platform has been gaining widespread coverage and Microsoft has made some forays into the forms market. Aria can be run on this platform via the J# language (a clone of Java) if required. On some mobile platforms this may be desirable where a JVM is not available or is a cost impediment.

However Java is by far the more mature platform and the available resources are far more extensive. In using .Net there is also the risk of getting locked into a Microsoft environment and the consequent costs. At the core, Aria being an open source platform gives you plenty of options.

Aria versus XUL , *XForms, XAML et al

Mozilla, Microsoft and others have produced a variety of XML formats for user interface description. These formats share many of the same objectives as Aria and unsurprisingly they use many similar techniques to construct applications. One of the advantages of XML in this context is that the formats are relatively open and so it is possible to render these documents with the Aria add-on libraries published by Formaria.

Aria differs from these libraries in a number of key ways. Firstly XUL and its variants rely heavily on client side scripting making them more difficult to program and maintain than the pure Java used in Aria. Many of the XUL variants are rendered with the Java Swing library making them unsuitable for resource challenged situations such as exist on mobile devices, Aria can be rendered with the AWT and hence memory and bandwidth are conserved. And of course XUL can't be compiled so it always pays the cost for parsing

Microsoft's XAML format deserves separate mention even though it is in many ways similar to XUL. XAML of course is not yet widely available. The licensing model offered by Microsoft may make use of XAML prohibitively expensive and the availability of XAML clients may be limited to the very latest Windows desktops.

Of course given Microsoft's strength in the market place the impact of Longhorn and XAML will be significant. The XAML architecture mimics that of Aria in many ways and provides many of the same features and facilities so we take this as an endorsement of the Aria architecture. In many ways Aria does for Java what XAML does for Windows.

The World Wide Web consortium is promoting XForms as part of the XHTML standard and again this offers a variation on the XUL theme. XForms embodies a rich data model and supports lifecycle semantics for a rich web client. Like XUL, XForms relies heavily on scripting, although to a much lesser degree than HTML forms. Again the add-on Aria libraries can render and interoperate with XForms. Aria differs in that its data model and update semantics are more cleanly separated in the industry standard MVC architecture making for a clean maintenance structure and clear programming structures.

A prototype filter is under development that allows Aria to read formats such as XForms, Infopath and XAML.

Aria versus Ajax

Ajax is something of a juggernaut powered by the hype surrounding Tim O'Reilly's Web 2.0 notion, however Ajax has been around quite some time and even featured in a precursor to Aria and Aria. Without doubt, Ajax is an improvement on vanilla HTML and even DHTML, but as has been mentioned above this comes at a price. Ajax applications are often quite complex, having a mix of client and server side technologies and a mix of programming metaphors and language. While this might be highly important to developers, end users probably couldn't care less about this complexity, so long as they can get the job done. Therefore Ajax has alot of merit and may well be the best choice for some applications. Conversely as applications grow and the shortcomings of the Ajax platform begin to exhibit themselves then a Aria or Aria based application begins to become more attractive.

Quite often the power user will demand better performance or better desktop integration than can be delivered by Ajax and here Aria can help. Therefore, if the system is not too complex and there is only occassional or light use of an application then Ajax could be a good choice. Where performance, or integration, or flexibility, or complexity are bigger issues then Aria may be the better option.

What may surprise some, who wrote of Java as slow and clunky, is that a Java application like Aria can be visually attractive, with lots of rich user interface features and great performance. Where that visual impact is important Aria is also the better option.

Other issues

The technology used in Aria is only one part of the overall application development story. There are many factors that affect how a project is implemented and managed. The following sections highlight some of these important factors to consider when embarking on a development project of any significance.

Legacy application support

Not everyone has the luxury of starting with a clean slate, and importantly Aria does not necessitate such an approach. The open architecture employed by Aria makes it easy to use Aria when and where possible. This may simply be the use of Aria to cleanup some code or it may be more extensive. Aria can be added incrementally to a project.

Legacy applications frequently suffer from maintenance issues such as spaghetti like code and the mixing of concerns so that it is often difficult to identify key business logic. Aria can greatly simplify this code by allowing much of the plumbing to be removed or hidden. Not only does this save code and reduce the complexity but it also make the business logic more apparent.

But why fix a working system? It's quite simple really; it's easier to maintain a small code base than a large one, and it's easier again to maintain a clean code base than a convoluted one. Using Aria as part of the cleanup means that the real value in an existing application can be preserved and prepared for tomorrow's challenges rather than facing a rebuild.

Aria's support for new technologies like POJOs, data binding and modern user interface components also mean that Aria can be a good choice for extending, reviving or refreshing existing applications.

Simple single language support

Many of today's applications and systems are composed of a variety of subsystems, often in different languages and invariably requiring multiple developers with different skill sets. In contrast Aria has a single language implementation. Java is used throughout and can be supplemented with simple XML (and this XML can be hidden from sight by use of the IDE).

Separation of roles

Aria promotes a clean architecture by separating development roles and concerns. As has already been remarked Aria makes business logic stand out by removing plumbing code. Aria goes further by separating the applications data, services and user interface into distinct components. Not only does this facilitate maintenance but it also helps promote good development practices.

Occasionally connected

Modern applications have to cope with a wide variety of connected devices. Despite the ubiquity of internet access it cannot be assumed that there is always a connection available. Even if there is a connection available quality of service considerations, or security concerns may mean that an application must cope gracefully with intermittent connectivity.

Aria not only supports this capability but provides sophisticated mechanisms for handling remote communications, routing and fault tolerance. Added to this is Aria's ability to support store and forward mechanisms so that once connectivity becomes available data can be sent to the server or updates retrieved.

Branding

Many companies sell through channels and these channels require branding of the application to suit the channel. Aria stylesheets facilitate this rebranding without change to the code.

Within Aria you can easily replace colors, fonts, text and other resources. This together with the separation of concerns enforced by the MVC architecture means that you can efficiently build an application to support multiple brands or sales channels. Furthermore the open architecture means that it is possible to deliver modular solutions whereby resellers and related businesses can integrate content in a coherent manner.

Performance and server load

Because Aria promotes the idea of a rich client and of performing more processing on the client side there is a consequent reduction in the load on the server. This locality of computing also leads to direct performance gains from a user perspective. Local calculations may not require a round trip to the server for data retrieval and once retrieved the data can be accessed efficiently. Moving processing to the client also removes several layers of processing (security, load balancing, logging etc...) from the server so not only can the processing/calculation be performed efficiently on the local machine but the server can also act in a more efficient manner by serving up coarse grained objects (more content and less overhead as requests are amalgamated).

Section II: Introduction

Installing Aria

Aria can be installed in a number of ways depending on the intended usage. At its simplest Aria can be downloaded and used as is. At the other end of the scale Aria requires a number of installations to access all features.

Downloading Aria

Aria can be downloaded from the SourceForge website at http://www.sourceforge.org/projects/aria . To download you need to register using a valid e-mail address. Once you have successfully registered you will be sent an e-mail with directions to the download site where you can download the version of Aria that you need.

As a registered user you will be able to subscribe to updates, newsletters and technical tips

Aria components

Aria is a Java platform and therefore requires a recent Java development kit. You need to have a recent JDK (1.5) to run all the features, notably the NetBeans plug-ins. Otherwise, if you do not plan to use the editors you can use just about any JDK you like.

Aria modules

Component

Usage

Aria

Aria can be used with just about any Java development environment. All you need do to begin working with Aria is add the AriaRuntimeCommon.jar module to your project's classpath. To use Aria in this way simple download the jar file and installed it somewhere that can be accessed by your development environment.

NetBeans plug-in module

The Aria editor is based on the NetBeans platform and is delivered as plug-in for the NetBeans IDE. Installation of this module is described in detail below.

Eclipse plug-in module

An Eclipse plug-in provides wizards for creation of projects and pages and supports editing and debugging of Aria projects.

Aria components

On the files release system you can obtain a number of variants of the Aria platform and these are described below. The files are delivered in two variants, one containing non-debug version of the class files and the second compiled with full debug information and include all logging and diagnostic features switched on:

Aria modules

Component

Usage

AriaRuntimeCommon.jar

This Jar contains all the class files needed for core Aria development using the AWT widget set.

The Aria Jars are compiled for a number of JDKs and accordingly have slightly different feature sets. The fullest feature set is for the most recent JDK.

For mobile and embedded devices the above packaging may not match a platforms capabilities so you may want to compile the source code for the platform that suits your needs. All the source and the build files are available, so you can tweak the feature set to suit your needs. For example, some mobile platforms include Swing and JDBC connectivity while others don't, depending upon the level of compatibility provided. Therefore to avoid proliforation of the number of versions compiled we have stuck to the core JDK when building pre-packaged versions of Aria.

The Aria jars are not signed, since you may need to sign the jars with a single certificate to distribute via Java Web Start. Therefore you need not stick to the packaging provided by the pre-built jars. For a minimal footprint you may even want to strip down the jars so that you only include thoses classes that you absolutely need. However, please ensure that you observe the licensing requirements in this regard if you change or alter the distribution.

Installing NetBeans

Aria includes a NetBeans plug-in for rapid development. Most of the features of Aria are used and accessed via this plug-in and it is therefore most likely that you will also require a version of NetBeans. At the time of writing the target version of NetBeans is version 5.5. Java version 1.5 (JDK 1.5) is also required to run this version of Aria

NetBeans can be downloaded from http://www.netbeans.org.

The NetBeans site also contains extensive documentation of installing and using the IDE.

Loading Aria

Once Aria has been downloaded. The NetBeans plug-in is delivered as a NetBeans module file with the .nbm extension. To load the module you must first start NetBeans. Once started go to the Update Center on the tools menu.

Then choose the ` Install Manually Downloaded Modules (.nbm files)' option. Select the four NetBeans modules: javax-jnlp.nbm, net-aria.nbm. The first of these are the module for the open source Aria module and the third is the module for Aria's add-on features.

When the NetBeans module has been successfully installed the Aria menu item will appear on the main menu.

If you are just installing Aria and AriaEditor, then you can check that the installation has succeeded by check that the New Aria Project wizard is available from the File | New | Project menu option.

Installing the samples

A number of samples are available on the Formaria website. The samples files are simple zip archives of complete projects. To begin working with the samples you can simple download the archive and unzip the archive and open the project using the NetBeans File | Open command. The samples provided with Aria are listed below:

Sample applications

Sample

Description

Hello World

The classic introduction.

Navigation

A simple example showing how to navigate within an application.

Translation

An example showing how translations can be setup and used.

Counter

A very basic calculator showing how to link in Java code to add custom logic to an application.

Address Forms

Some simple forms showing the key concepts of data binding and validation.

Mortgages

A more complete example showing how to build a working example. This example is also used as a tutorial on how to use Aria.

In addition to the samples listed above the source code for the case studies included in this guide can also be downloaded. New samples are occasionally added to the website and it is probably worth checking for updates (http://www.formaria.org). The Formaria website also features numerous technical articles describing new and key features.

Installing the Eclipse plug-in

Like the NetBeans plug-in the Eclipse plug-in provides interactive editing of Aria and Aria applications. The plug-in is easy to install, just unpack the zip archice and drop the enclosed jar into Eclipse's plugin folder and restart the IDE.

When Eclipse restarts you should see the Aria project wizard under the File | New | Other dialog. At present there is no Eclipse version of the Aria plugin.

Building Aria from source

Building Aria from the source code is possible within both the NetBeans and the Eclipse IDEs. All of the source is available from the SourceForge repository. The project's SourceForge.net Subversion repository can be checked out through SVN with the following instruction set:

Subversion access

 svn co https://aria.svn.sourceforge.net/svnroot/aria aria

(Warning: This is a generic Subversion checkout command which will pull all modules, tags and/or branches of the project).

Project source layout

The source code is laid out in a number of packages, as follows:

Top level projects

Folder/Package

Role

 AriaRuntimeCommon

Contains all the source code, resources and build files for the runtime libraries.

 AriaEditorCommon

Source common to both the NetBeans and Eclipse projects

 AriaEditorEclipse

An Eclipse specific project for building the Eclipse plugin

 AriaEditorNetBeansSuite

A collection of projects for building the NetBeans plugin. Most of the sub-projects are merely wrappers for the plugin's dependancies.

 AriaMobileMidp

A version of Aria for MIDP. This project is developed in parallel to AriaRuntimeCommon and has diverging source.

 lib

A collection of libraries needed for the Aria projects.

 other

Contains non Java code for `other' clients stubs.

 samples

A collection of sample projects and utilities

 docs

The source FrameMaker documents that go to making up this manual.

Building the runtime libraries

The runtime libraries are built using the latest version of NetBeans (6.1 at the time of writing), using the AriaRuntimeCommon project. The project also uses JDK 1.6, although it compiles for earlier JDKs.

To build the runtime you will need to download the main project plus the following libraries or packages from the lib folder described above in See Top level projects.:

Libraries used by the AriaRuntimeCommon project

Library

Description

beanshell

Libraries for BeanShell scripting support

commons

Apache Common libraries. including commons-codec .

google

The Google translation service API

hsqldb

The hsqldb library which is used in some samples for off-line or lightweight database access. Aria includes some support for packaging hsqldb databases in the jars distributed with Java Web Start

itext

The iText library for PDF generation (export functionality)

jakarta

The Jakarta regular expression library, plus the BCEL library for byte code manipulation (used for DTO class generation).

jdic

SwingLab's JDIC project used for system tray and browser integration.

jmf

The Java Media Framework used for video and audio support.

netscape

Netscape's JavaScript integration support

jxl

Excel export

jgoodies

JGoodies look and feel support libraries

multisplit

SwingLab's MultiSplit layout manager (incidentally maintained by Aria contributors).

swt

Eclipse SWT libraries

tomcat

The servlet API

svgSalamander

SVG widget and rendering support

tablelayout

An advanced layout manager

timingframework

SwingLab's animation and timing library

xalan

XML support

In addition to these libraries, some libraries from the Java runtime and JDK are employed. All of these libraries are referenced from relative paths, so if you download the libraries, preserving the directory structure the project should build without modification.

The default build target will generate the runtime libraries in a number of forms:

Runtime libraries produced by the default build

Library

Contents

AriaRuntimeCommon.jar

The complete runtime library include all versions of the widgets.

AriaRuntimeCore_<version>.jar

The core library minus any widgets

AriaRuntimeAwt_<version>.jar

The AWT specific widgets

AriaRuntimeSwing_<version>.jar

The Swing specific widgets

AriaRuntimeSwt_<version>.jar

The SWT specific widgets

The version number is appended to the library name in the form version_date, for example AriaRuntimeCommon_1.0.0.v20080726.jar . At present the build produces a Java 6 version , but this will be extended to include support for earlier JDKs and when this is done the JDK version will also be appended to the jar name.

The default build will also copy the AriaRuntimeCommon.jar file to the libs/aria folder which is references by many of the sample projects and applications, so that these applications are always using the latest build. Periodically this library is also commited to SVN (when any significant change is made).

Building the NetBeans plugin from source

Once you have built the runtime environment as described above you can begin to build the NetBeans plugin. The plugin relies on the runtime, running on the same code as the end user applications, but for distribution as part of a plugin the runtime must first be bundled as an installable module for NetBeans.

Indeed, this process of wrapping the runtime libraries or dependancies is used for all the extrenal jars needed by the plugin and for wrapping the code shared with the Eclipse plugin. A suite of modules is therefore used to create the plugin, and this is called the AriaNetBeansSuite .

The source and project files for all plugin modules is again available from the SourceForge SVN repository (see See Subversion access.). To build the project first open the AriaNetBeansSuite project, when this is opened you will be prompted to open all the module projects within the suite. Most of these modules are merely static wrappers for the plugin's libraries, apart from the following:

Non-static projects for building the NetBeans module suite

Folder/Package

Role

 AriaRuntimeCommonModule

Contains all the source code, resources and build files for the runtime libraries.

 AriaRuntimeCommonModule

A wrapper for the runtime library, the library wrapped by this module is updated dynamically when the module builds, so it wraps the latest runtime library.

 AriaEditorCommon

Source common to both the NetBeans and Eclipse projects

 AriaEditorCommonModule

A wrapper for AriaEditorCommon project, operating in the same way as the runtime module so that the wrapper uses the latest build of the common module

 AriaEditorNetBeansSuite

A collection of projects for building the NetBeans plugin. Most of the sub-projects are merely wrappers for the plugin's dependancies.

All the projects have dependancies on the required modules so doing a clean and build on the suite will build everything, including the runtime environment.

The project can be built within NetBeans project view by right clicking on the AriaEditorNetBeansSuite project and choosing Build NBMs . The result of the build is a set of NBM files in the AriaNetBeansSuite/build/updates folder together with the updates.xml file.

Once the modules have been build you can either deploy the contents of the directory or launch the editor in a hosted version of NetBeans by clicking either Run or Debug

Building the Eclipse plugin from source

The process for building the Eclipse plugin is pretty much just a case of downloading the sourec from SVN, opening the project and building. The Eclipse plugin is a single project contained in the AriaEditorEclipse folder.

One possible complication is that the ARIA_PROJECT_ROOT variable needs to be set. Within the Eclipse Window | Preferences | Java | Build Path | Classpath Variables dialog the variable can be added or configured to suit your environment's setup.

Building the sample applications from source

t present the sample applications are only setup within NetBeans. Most of the applications are self contained and refer to libraries within the Aria SVN hierarchy (within the libs folder). One exception to this is the MetroBank example that contains a web server application which relies on the Spring framework.

Since the Spring framrwork is a large and complex environment it is not included with the Aria SVN repository and is instead referenced by the project via some private settings. Within the nbproject/private/private.properties folder there are some settings that will need to be adjusted to match your setup before the build will success. These settings are:

External Spring Dependancies

Setting

Role

spring.libraries=C:\\Tools\\spring-framework-2.5.4\\lib

Points to the root folder of the Spring distribution. Use Spring 2.5.x. See http://www.springframework.org/

spring.modules=C:\\Tools\\spring-modules-0.9-with-dependencies

References the root of the Spring Modules installation. (see https://springmodules.dev.java.net/)

spring.security=C:\\Tools\\spring-security-2.0.2

References the root of the Spring Security installation (formerly Acegi, see http://static.springframework.org/spring-security/site/).

The Metrobank sample is split into two parts, the client and the server parts. More info about this example can be found later in this manual

A quick tour of the editor

This chapter provides a quick tour of the Aria editor within NetBeans. The main features of the editor and the main steps involved in creating an application are described. The chapter is intended only as an introduction so that you can begin exploring the features built into the editor. Later chapters will cover each of the topics in more detail.

Creating a new project

NetBeans includes a range of templates for creating various pieces of applications. The Aria templates are unsurprisingly listed under the Aria heading.

To access the templates choose File | New from the main menu.

The templates included in Aria are as follows:

  • New Project: Creates a the stub of a new project
  • New Page: Creates a blank page within the current project

To create a new project choose the New Project template. The template is shown below.

To complete the template you must choose a directory into which the new project will be generated. To get started quickly you can just choose the default settings once you have set the target directory.

For the most part the options for creation of the new project are simple.

The settings page also gives options for the project name, which is the name that will be used to identify the project within NetBeans. The application title is the text that appears in the application title bar, this is also the text that some operating systems used to identify the running application. The remaining settings affect the way the application is presented and behaves.

When the application first appears it can be centred on screen or it can appear in the top left of the desktop, the ` Center on screen ' checkbox controls this behavior. The application may also be embedded in an HTML page as an applet or it may be run as a standalone application. In either case it is possible to show the applet/application in a popup window. This window does not have the usual sizeable window border. The ` Popup Window ' checkbox controls this option.

The ` Window Size ' section is straightforward and simply lists some standard window sizes for the new application.

Optionally a Splash Screen can be include. The splash screen is simple an image that is presented as the application loads, the screen typically times out after a few seconds, or it can be dismissed with a user click.

By default the first page displayed is called ` welcome.xml ', but you can choose to display a different page at startup by entering the name of the first page. There is no need to specify the ` .XML ' suffix as this will be assumed, if there is no Java class called by the name of the startup page.

One option you should take care to set correctly is the package name. This can often be confused with a path and instead it is the Java Language package name. For anyone not familiar with package names the appendix gives a brief overview of this language feature

The ` Log level ' controls the amount of information that is displayed in the console while running the application. This applies only when running the application with debug versions of the libraries (the default in Aria).

The Frameset Configuration optionally allows you to configure your application as multiple pages within a single frame or as a single page. Sometimes a frameset is used when common elements repeat across multiple screens, say for example a navigational control panel or a banner headline.

The project is configured with multiple files to help separate the various forms of content. The most important of these are listed in the ` File name configuration ' page.

The final page of the new project wizard provides a place to configure some add-ons for Aria. These options are intended for more experienced users but essentially they allow you to extend the types of components, data bindings and validations that can be used in an application. The parameters are all class names. More details about these options will be given in later chapters.

Once the new project has been created the various folders and configuration files are setup. Some page stubs will also be created ready for you to start building content.

If you have any doubts about the options when creating the project there is no need to worry as all the options available in the new project wizard are also available on the project page. This page is opened once the initial generation of the project is finished.

Once the project is created it is shown in the Project view, which shows all the classes and packages within the project. The Files view shows all the files and resources that are used in the project.

The build.xml file is listed in this view and can be used to trigger actions such as the compiling, building and testing of the application.

A number of directories are created under the project's main directory or folder. The directories are as follows:

Project folders

Folder

Usage

Pages

Stores the XML page declarations.

Lang

Stores the language files used for translation of the application.

Resources

Holds various resources used in the project including graphics, configuration files and so on.

Src

The source code for the Java classes is held in this folder. Another folder (classes) may also be created depending on the configuration. The classes folder is a temporary folder and may be deleted at any time.

The project view

Once a new project has been created the project is automatically opened. The project editor contains much of the information that was in the New Project template and allows you change the project settings at any time during the project lifecycle.

At the top of the project view there is a set of buttons for access to additional parts of the project configuration. The options available in these windows affect all parts of the application, such as the page size and the frameset layout. Some of the options also affect the runtime behavior by adding extensions and modifying the classpath.

Note: Pages can be opened from either the Fi le View , but while they are visible in the Project View , they will not open and instead a message is issued requesting that you open the files from the File View instead. On non English versions of NetBeans this check will fail and you make experience problems. This is a known issue that we will attempt to fix.

Pages and resources

Creating a new page in Aria is again achieved by choosing File|New. A template is also used to create the page, but this time all you need to do is choose a name for the page and its location. The page should be placed in the `pages' subdirectory of the project.

Later we will see how to add rules and Java code to the page and your will learn that the page name is used as the basis for a Java file. As Java is particular about class names it is best to conform to Java's naming conventions when choosing a page name. Thus the name should be a single word, starting with a capital letter.

The page designer

Once a new page is created it is opened for editing in the editor. Here's an example of a simple page.

At this point it is worth noting some of the overall features of the editor.

Areas of the editor

Section

Description

A

On the left is a hierarchical view of the project and the filesystems used by the project. The filesystem corresponds to the Java classpath used by the project. Underneath the project folder are various folders into which the different categories of files and resources are saved. Notice the pages folder which naturally enough contains the pages belonging to the project.

Below the navigator, you will find the Inspector window which shows the structure of the page being edited. Sometimes the Inspector can be useful for selecting components particularly if components overlap or obscure one another. The Inspector also provides access to features such as locking and unlcoking of the components.

B

In the centre is the editor area. The screenshot above shows the page designer. The page designer is docked in this area and other editors can be selected via a set of tabs along the top edge of the editor.

As the editors are activated or deactivated they may cause other windows to appear or disappear according to the context. While the page designer is active several other windows are shown and docked into the right hand side of the main window.

C

On the right hand side are three windows. The component palette, the component inspector and the styles properties window. Also visible but not active is the component properties window which shares screen space with the style properties window.

Pages can be opened on their own or within a frameset. To open a page with a frameset right click the page within the Files View and choose Open in frameset .

NetBeans also provides many features that will be of use to you as a developer from time to time. For example the Runtime view (not shown above, but accessible from the Window menu) provides access to runtime resources such as databases and servers. Using this view you can for instance drag a database table to a component on your page to establish a data binding, but we will cover this in more detail later in this manual.

Components

At the top right of the editor, when the page designer is active you will see the component palette. The content of the component palette will depend on the type of application being developed and whether or not any extensions have been added to Aria.

As a minimum there will be several core components shown. These are:

Component palette icons

Icon

Component description

 

A button object, usually used to initiate some event or action.

 

A check box, indicating an option selectable by the user. Where options are mutually exclusive a set of radio buttons should be used.

 

A drop down list or combo box, representing a list of choices. A drop down list is usually used where there is a small number of options or where the number of options is too great for a set of radio buttons.

 

An edit field, an input field where the user can enter a simple value. An edit field is a key input mechanism and Aria provides support for validation of input data using edit fields.

 

An image component. Used to display images stored in the resources folder. Can be used with a MouseHandler to interact with the user.

 

A hotspot, a clickable area that can be placed over another object so that when the user click on the hotspot some action occurs.

 

An imagemap, like a hotspot except that an irregular/polygonal area can be specified as the clickable area. Like a hotspot the user can click to invoke an action.

 

A Label, a simple piece of text. The label text does not normally cause any interaction with the user

 

A Panel, a container into which other components can be placed. The panel can optionally be made to display a frame or border.

 

A password edit field, a special case of an edit field used for entering a password. As the user types the value of the password the field displays a mask character for each letter or character typed.

 

A scroll panel, a special form of a panel that supports scrolling. The pane can optionally be scrolled horizontally or vertically. The scroll pane automatically adjusts itself to the size of its content.

 

A table, a simple table component that can display data directly from the model. Headers and alternate row shading are just two of many features that the table control supports.

 

A text field, a simple text field. This component supports multiple lines of text.

The user can add any of these components to the page by first selecting a component from the component palette and then placing it by clicking at the desired location on the page.

Components can be nested by placing panels on the page and then placing additional components on the new panel. The panel is then considered to own the new components or children. When the panel is moved so too are the children.

As each component is selected the properties of that component are displayed in the properties palette, the style palette and the component inspector. Properties can be changed by choosing options within the properties palettes or by double clicking on styles. The component inspector is primarily a mechanism for showing the hierarchical relationship between components.

Multiple components can be inspected at once but only the common subset of the properties will be available in the various palettes and you may not always be able to interact with all properties.

When fully configured Aria may include several sets of components or widgets. The components belonging to these widget sets are displayed on separate tabs within the component palette. The availability of some of these widget sets may depend on the project configuration, for example if an AWT application has been chosen then the Swing widgets will not be available.

Styles

Styles in Aria comprise sets of colors and fonts that can be applied to just about any component. The style palette provides a simple way to interact with the styles, by simply selecting a component and double clicking a style value.

Styles can also be changed by simply changing the style file or by changing individual styles (by right clicking on the style palette). Aria even includes a color scheme picker (which again can be accessed by right clicking on the style palette).

The color scheme picker is designed to allow you to quickly choose and experiment with styles that will produce a visually attractive application.

Source code

An application generally isn't a lot of use unless it can do something. Aria is designed to make it easy to build applications and therefore adding custom functionality and business logic is simple.

When you select a component you will see its properties in the property sheet. Depending upon the component type this property sheet may display some event properties. For example a push button will have an ActionHandler property. By typing in the name of the source code method you can edit the Java source code for that method, and if no such method exists the stub of a new method of that name will be inserted into you Java source code file.

Later we will see how such an event handler is actually connected up to the user interface. However it is worth noting that Aria declares such links in the page's XML. You can switch back and forth to this XML by clicking the ( Design and XML ) buttons at the top of the page designer.

Of course you can also find the Java code in the Files view and open the file directly. The file is just a normal Java file and there are no hidden or special sections to worry about.

For example to add a piece of application logic simple choose the component that will initiate the action and within the properties palette find the appropriate response and just enter the name of the response method. Upon pressing Enter the source editor is opened and you can edit the Java code belonging to the event. At any stage you can switch back to the page designer by clicking on the tabs above the source editor or by closing the source editor.

The pageDesigner also allows you to selecting existing functions and attach then to a component. A list of available functions is displayed in the drop when you click on the event handler in the proeprty sheet, so if you do not want to enter the name of new event just choose one of the predefined functions if appropriate.

Once you have added a function and wish to jump to the source code, just hold the CTRL key while you click on the event handler and this will open the source code editor instead of editing the handler name.

In later chapters we will see just how to begin coding custom business logic.

XML

As a technology Aria makes extensive use of XML. XML is used to describe a wide variety of things in Aria, including the user interface. The XML for a UI can be displayed by choosing the XML tab for that page. Furthermore Aria allows two-way editing of the XML whereby any edits to the XML will be reflected in the page and any changes to the page within the page designer will be displayed in the XML. The updates occur as the display is modified by choosing the tabs along the top of the editor.

Later chapters will explain Aria's use of XML in greater depth.

Compilation

Writing code for an application is the first step in making working code. Java is a compiled language and therefore the source code needs to be compiled. The quickest and easiest way to do this is to right click on the project node in the Aria view. The context menu then shows a compile all option and choosing the Compile or Execute option will compile all classes within the project.

NetBeans also includes many options to control compilation, building and testing of an application and you should refer to the NetBeans documentation for more information on the available options.

Testing

Once the project has been successfully compiled you should test it prior to distribution. Aria and the underlying NetBeans platform provide a powerful testing and debugging environment. To test an application simply choose the Debug Project option from the popup menu on the tree icon for the project in build.xml file in the Files View.

Deployment

Finally to make widespread use of your application you need to package and deploy the application. There are wide variety of deployment scenarios and these will be covered in detail in later chapters.

Again NetBeans provides additional plug-ins to help manage the deployment of application, including Java Web Start deployments, and you may wish to download some of these extra plug-ins. Aria generates a stub of a JNLP file that can be used by such plug-ins.

Running Applications

Within Aria starting an application is handled by NetBeans using the normal Run or Debug commands. Outside of NetBeans several methods can be used to start-up Aria applications depending upon how the applications will be deployed. All of these start-up sequences start by invoking the *Applet class and eventually follow a generic initialization process. Some minor variations are accommodated by the Applet class to handle the different ways of setting start-up parameters and the differences in the classloaders.

When starting a Aria application the choice of the Applet class is important as this dictates whether or not the Aria framework constructs Swing or AWT components. If the org.formaria.awt.Applet class is used then AWT components are used, whereas if the org.formaria.swing.Applet version is used Swing components are constructed.

In the examples above it will be assumed that AWT is being used, but the examples will work just as well with Swing if the Swing version of Applet is substituted.

For the purposes of simplicity it will be assumed in the following discussions and examples that Java is installed as default and that Java can be started without additional modification of the environment or path.

Command-line startup sequence for applications

To start an application from the command line it is only necessary to invoke the Applet class.

Command Line startup command

  1. java -cp AriaRuntimeCommon.jar org.formaria.awt.Applet startup.properties

where startup.properties is the name of the initialization file (which if omitted will default to startup.properties ). The application can take one additional parameter, the default package name. Make sure to use the -cp or -classpath switch instead of the -jar switch (unless yo build a custom self-starting jar).

The default package name for an application is normally set by whichever version of the Applet is used to start the application ( org.formaria.awt for the org.formaria.awt.AXpplet class and org.formaria.swing for the org.formaria.swing.Applet class). It is very rare that this parameter would need to be set as the application will automatically set the appropriate default value.

Note that the startup file is generated by Aria each time the project is built, however Aria actually stores the values is the project.aria file for its own use.

Java Web Start Start-up Sequence

To run a Aria application as a Java Web Start application several preparatory steps are required. Firstly, the application must be packaged as an appropriate set of Jar files.

Next the application Jar files and resources must be signed and placed on a server configured to serve JNLP files. In particular, pay attention to how the mime-type is set for your particular server. In some cases the mime-type can be set by using dynamic content on the server such as JSPs or ASPs instead of vanilla HTML.

Once the Jars files are on the server the JNLP file can be configured. Here's an example:

Java Web Start (JNLP) startup code

  1. <?xml version="1.0" encoding="UTF8"?>
  2. <!-- created by Aria -->
  3. <jnlp spec="1.0+" codebase="http://myserver/mydeploymentfolder/" >
  4. <information>
  5. <title>Aria 1.0</title>
  6. <vendor>My Company</vendor>
  7. <homepage href="http://localhost/mywebfolder" />
  8. <offline-allowed />
  9. </information>
  10. <resources>
  11. <j2se version="1.1.4+" />
  12. <jar href="/Project.jar" main="false" download="eager" size="12369" />
  13. <jar href="lib/AriaRuntimeCommon.jar" main="true" download="eager" size="100131" />
  14. </resources>
  15. <application-desc main-class="org.formaria.awt.Applet" />
  16. <security>
  17. <all-permissions />
  18. </security>
  19. </jnlp>

To actually launch the Web Start Application it is then just a question of invoking the JNLP file. Normally this is done by embedding the link in an HTML page.

Normally it is also the practice to check that JWS is installed and display an appropriate error message if it is not. Sun's JWS web pages give examples and documentation of how to do this.

HTML/ Applet Start-up Sequence

The HTML initialization sequence is like the JNLP in some ways except of course that it is an applet that is being started and not an application. Security settings are also different for applets depending on the environment in which they run.

The HTML fragment required for an applet if as follows:

Basic applet tag

  1. <APPLET height=480 width=640 archive="lib/AriaRuntimeCommon.jar,
  2. build/distrib.jar" code=org.formaria.awt.Applet>
  3. <PARAM NAME="startfile" VALUE="startup.properties">
  4. </APPLET>

Again the Jar file should be signed for distribution and placed on the webserver.

Generic Initialization

Once the Applet class has been invoked the initialization of the application/applet is pretty much the same regardless of how it was started. The sequence (and purpose of each step) is as follows:

  1. Register the component factories. The registration process involves reading the ' NumComponentFactories ' parameter from the start-up file and then reading, instantiating and registering the number of component factories listed in the start-up file. Implicitly either an AWT or Swing component factory is already registered
  2. Setup the project class. The project class maintains references to all the other resources, managers and factories used by the application. The project manager class can be accessed through a static accessor method and with this mechanism any resource can be easily obtained from the project without the need to carry references to the various objects.
  3. Setup the page manager. The page manager controls the application display areas and acts as a broker for displaying frames and pages. The page manager also provides some navigation and page history support. At this point the content is also setup by specifying a frameset (if any) with the ' Frames ' parameter and the content with the ' StartClass ' parameter.
  4. Setup the resource access. The resource access provides a simple method of loading resources and hides details of the various classloaders, paths and so on. Resource access is managed by the *Project instance which does some caching of resources such as images that are frequently reused.
  5. Setup the style manager. This class loads style information from a file indicated by the 'styleFile' parameter in the start-up file. Styles can be used to configure fonts and colors in a consistent manner.
  6. Size the main window. The startup parameters ' ClientWidth ' and ' ClientHeight ' are used to set the size of the main application window. The main window (in the case of an application) can also be centred by setting the start-up property ' CenterWin ' to true.
  7. Set the layout. Assuming that some content was configured the application/applet is now laid out. This process will position the various elements of a frameset and the content of the frames or the content of the main window if no frameset is being used.
  8. Add a shutdown hook. A shutdown hook is largely used for debug purposes to allow output of logging information. Shutdown hooks were not available on earlier JDKs so this feature may not be in use on all environments (particularly embedded platforms)
  9. Display the main window. The application is now ready to display and the main window is shown. If you need to display a splash screen one good place to initiate its display (without subclassing Applet ) is when the first page is created, by embedding code in that page's pageCreated or pageActivated method.

Aria 3.0 refactored must of the startup infrastructure so that there is very little need to know any of the above detail, even if you are building a custom setup or a custom application type. Aria provides several stubs for the propular widgets sets and the normal application styles, however should you need something other than this then you can implement your own startup process quite easily as most of the necessary functionality is encapsulated in the ApplicationContext class which is used to setup the common infrastructure by the Aria framework. An example of this is the sample application that integrates Aria into the run-of-the-mill Swing application as a panel embedded in that application (see See Embedded applications.).

Startup file stubs

Aria creates a number stubs of the startup file whenever a project is generated. These files are found in the root directory of the project and are regenerated each time the project is generated.

Eclipse Debugging

In Eclipse the process of debugging is somewhat different to the NetBeans Experience. To debug the project simply right click the project in the Navigator or Package Explorer and choose the Debug As option and select Java Application .

Before Eclipse can run the application it needs to know the main entry point into the application, and to find this it will pop up a dialog requesting that you select the main class.

The Aria libraries should be on the project classpath and therefore Eclipse should be able to locate the Applet class as shown above. Note that for other application styles you may need to specify additional startup parameters and in this case you will need to create a custom debug configuration - see the Eclipse documentation for more details on doing so.

Getting Started

In this chapter we will see how to build some simple Aria applications using both Java and XML. We will see all the features needed to build a fully functional application within Aria in action. The chapter provides an introduction to the setup mechanisms used within a Aria application. Most of the features described can be configured interactively but to aid understanding they are explained at a lower level.

The chapter assumes that you have already encountered the features described in the previous chapter and that you have a basic understanding of how Java based applications function.

Don't worry if you do not understand all the nuances of the technology described as the various pieces of the application are but together. Later chapters are devoted to the individual features employed.

If you are new to Java we recommend that you study some of the numerous educational resources available on the Internet, some of which are listed in the appendix on Java.

Hello world

Traditionally the first example used in many user guides is the infamous 'Hello World' example where the task is to emit or display the simple text 'Hello World'. In Aria this is very simple, all you need do is create an XML file called 'home.xml', preferably in a new folder. The XML file contains the declaration of the user interface needed for this task. It looks like the following:

Page user-interface declaration

  1. <Page>
  2. <Components>
  3. <Label x="32" y="37" w="100" h="29" content="Hello World"/>
  4. </Components>
  5. </Page></P>

And that's just about it. The only other thing that is needed is a convenient way of starting the application. To do this we create a startup command file. This command file doesn't have to do very much other than invoking the Java environment and telling it what class to start. The start class in this case is the entry point to the Aria framework or library. It contains the following command:

Command-line startup

  1. java -classpath .;AriaRuntimeCommon.jar; org.formaria.awt.Applet

The AriaRuntimeCommon jar is the minimum level of library support that can be used with Aria. It is a version of the Aria core compiled for early JDKs (including the Java environment embedded in Microsoft's Internet Explorer).

To use a fuller more functional JDK, for example to access the Swing toolkit instead of the AWT simply change the startup file to use this command:

Command-line startup for Swing

  1. java -classpath .;AriaRuntimeCommon.jar; org.formaria.swing.Applet

Running this command will create a Swing user interface (UI) for the application. The two versions of the application should look pretty much the same.

This little example immediately shows a number of the benefits of Aria and XML. Most obviously the UI declaration is very small and compact. Consider all the code needed to get such an example up and running in many languages. Secondly the approach of separating the UI declaration from the implementation makes it easy to switch that implementation, as shown above in switching from the AWT to Swing toolkits.

Hello world redux

Another much touted benefit of Aria is its close integration with Java and the ability to declare the UI in XML or in Java. To emphasize this let's see how we can build the same thing in Java.

This time instead of creating ' home.xml ' we need to create a Java class. Using the normal naming conventions and capitalization we will call this class ' Home.java '. The Java code needed is as follows:

Java UI class

  1. import org.formaria.aria.*;
  2. import org.formaria.awt.*;
  3.  
  4. public class Home extends Page
  5. {
  6. public Home()
  7. {
  8. addComponent( Page.LABEL, 32, 37, 100, 29, "Hello World" );
  9. }
  10. }

Now while this isn't as compact as the XML declaration it is nonetheless a little more compact than the traditional Java code used for such an example.

To run this example there is of course another step required, that is, to compile the Java code. This can be done from the command-line, in the root directory of the project. Just type the following command:

Command-line startup for compilation of the Java class

  1. javac Home.java

This should create a file called ' Home.class ' in the same directory. The application can then be run in the very same way as the XML version. The underlying Aria will try loading an an XML file in preference to a Java class, so if it finds your XML file first then that will be used instead of the Java class even if the two files are in the same folder.

Beyond HelloWorld

While the HelloWorld examples offer a minimal way of getting started with Aria it is not normal. Aria can use many different resources and settings to configure and run an application. The HelloWorld examples relied on default settings for most of the Aria configuration.

Normally Aria applications are configured via a startup properties file. This file can be specified as a command-line argument:

Command-line startup using a startup properties file

  1. java -classpath .;AriaRuntimeCommon.jar; org.formaria.awt.Applet startup.properties

Or, if no command-line argument is provided Aria will look for ' startup.properties '. (And remember that the startup file is regenerated by Aria whenever an application is saved. Aria itself uses a file project.aria to store the properties that are written to the startup.properties file).

It is also useful to separate the various components of an Aria project into separate sub-directories. Thus the XML declarations for pages are put into the ' pages ' sub-directory, while the Java source files are places in the ' src ' sub-directory. Other resources such as images are placed in the ' resources ' sub-directory and finally localization files are placed in the ' lang ' subdirectory.

When using Aria (as we will see in the next chapter), a special class/resource loader is built-in and can locate files in these subdirectories without the need for them to appear on the classpath.

The startup file contains settings that control much of how Aria is configured. Some of the more commonly used configuration settings are shown below and could be used for the HelloWorld example.

Startup properties

  1. CenterWin=true
  2. ClientHeight=480
  3. ClientWidth=640
  4. StartClass=Home
  5. Title=A simple HelloWorld example
  6. StyleFile=styles.xml
  7. UseWindow=true
  8. StartPackage=aria.projects.helloworld

The purpose of these settings is as follows:

Startup parameters

Startup parameter

Usage

CentreWin

(true|false) Centers the application window on the screen when set to true.

ClientHeight

Set the height of the application window in pixels.

ClientWidth

Set the width of the application window in pixels.

StartClass

Sets the name of name of the initial class or XML file to display.

StartPackage

Sets the name of the package used for this application's Java classes. In the case of the HelloWorld examples no package was used so this setting could be omitted. The above example indicates that Aria should look for a class called 'aria.projects.helloworld.Home.class'.

UseWindow

( true|false ) Tells Aria to use a Window without a border or use a normal application frame to hold the application.

StyleFile

Points to the name of a style file used to configure the colours and fonts in the application.

Building a simple address form

Most applications require user interaction and some data capture. Typical of this is entry of user details including name, telephone numbers and addresses.

This example goes beyond what was used in HelloWorld and shows some of the major steps in creating an attractive UI. We will start by creating a very simple form and then we will apply some style information to improve the look. In later sections this example will be expanded to produce a fully working, full featured form.

To start with we need a declaration of the input fields needed to capture the user input. We will use XML for this, and at its simplest this looks like the following:

An address form/page declared in XML

  1. <Page>
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right"/>
  4. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right"/>
  5. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right"/>
  6. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right"/>
  7. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right"/>
  8. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right"/>
  9. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  10. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  11. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  12. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  13. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  14. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  15. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  16. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  17. </Components>
  18. </Page>

In the above declaration the Label fields are all right aligned and the edit fields are all named. We name the edit fields as later we will want to interact with them programmatically.

This form is basic and does not include any formatting specific to any region. The address is comprised of three lines whereas most applications would further customize addresses for things like post or zip codes. We leave such customization as a user exercise as it will not affect the overall behavior of the form.

Note also that the form is laid out using absolute positioning and that in general it is better to use layout managers for the positioning of components (again, this will be covered in later chapters).

Generally the order in which the components are declared is of little importance, but it is in this order that the components are added to the container and therefore this dictates the z-order of the components. The z-order may be of interest if components overlap.

Applying style

To improve the appearance of the form we apply some styling information. This style information simply consists of the name of a style to use when rendering the component.

Aria includes a style editor for interactively setting up the colours and fonts that comprise styles. These styles can then be interactively applied to a component or alternatively the styles can be applied via the Java code as the application executes.

The style details are stored in an XML file pointed to by the startup parameter ' StyleFile '. In the absence of a file name the files ' styles.xml ' is used. A typical style file includes colour and font information as below:

A typical style file

  1. <styles>
  2. <style name="banner">
  3. <color_back value="ffffff"/>
  4. <color_fore value="0000ff"/>
  5. <font_face value="arial"/>
  6. <font_size value="10"/>
  7. <font_weight value="0"/>
  8. <font_italic value="0"/>
  9. </style>
  10. <style name="base">
  11. <color_back value="ffffff"/>
  12. <color_fore value="0000ff"/>
  13. <font_face value="arial"/>
  14. <font_size value="10"/>
  15. <font_weight value="0"/>
  16. <font_italic value="0"/>
  17. </style>
  18. <style name="Caption">
  19. <color_back value="ffffff"/>
  20. <color_fore value="008800"/>
  21. <font_face value="arial"/>
  22. <font_size value="11"/>
  23. <font_weight value="0"/>
  24. <font_italic value="0"/>
  25. <style name="CaptionSmall">
  26. <font_size value="8"/>
  27. <font_italic value="1"/>
  28. </style>
  29. </style>
  30. <style name="footer">
  31. <color_back value="ffffff"/>
  32. ...
  33. </styles>

Styles are determined from this hierarchy so that in the above example the style ' CaptionSmall ' is of the same font and colour except with a smaller point size and italicized.

To apply a style to a component you just need to reference the style in the page's XML declaration. Thus applying style to the labels the above address form becomes:

An XML page using styles

  1. <Page>
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right"
  4. style="Caption"/>
  5. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right"
  6. style="Caption"/>
  7. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right"
  8. style="Caption"/>
  9. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right"
  10. style="Caption"/>
  11. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right"
  12. style="Caption"/>
  13. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right"
  14. style="Caption"/>
  15. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  16. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  17. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  18. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  19. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  20. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  21. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  22. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  23. </Components>
  24. </Page>

Validating user inputs

When a form's data is eventually processed it helps if the data is appropriate for the intended process as much error checking can be eliminated. It also helps if the user is informed about input errors as early as possible.

Aria provides several levels of input validation. Firstly some of the input fields, like edit controls can be configured to accept a limited range of inputs (numbers, dates, passwords) and secondly the input can be validated using declarative validations.

Validations are rules that are applied to input fields. Aria takes the approach that these rules are reusable and therefore the validation rules are declared separate to the UI. The startup file is used to point at the validations declaration or by default a filename of validations.xml is assumed. Some examples of the validation rules are:

A validation rules file

  1. <Validations>
  2. <validation name="age" type="minmax" min="18" max="100"
  3. msg="Age must be between 18 and 100"
  4. mandatory="true"/>
  5. <validation name="firstname" type="mandatory"
  6. msg="Firstname cannot be blank"/>
  7. <validation name="surname" type="mandatory"
  8. msg="Surname cannot be blank"/>
  9. </Validations>

The exact content and attributes of the rule depend on the type of validation being configured. The first example shows a min-max rule and specifies the limits for the range. The second and third examples are simple checks for the presence of an input value, a mandatory or obligatory value. Each of the examples includes a message that is shown in case an error is detected.

Validation rule checking is triggered whenever an input field loses input focus or when the user attempts to change the page or navigate to another page.

Of course the rules have to be assigned or attached to the appropriate input field and this is done in the page's XML file. For simplicity we will just assign the name checking but the complete example contains a fuller set of input validations. Thus the page becomes:

An XML page using validation rules

  1. <Page>
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right"
  4. style="Caption"/>
  5. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right"
  6. style="Caption"/>
  7. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right"
  8. style="Caption"/>
  9. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right"
  10. style="Caption"/>
  11. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right"
  12. style="Caption"/>
  13. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right"
  14. style="Caption"/>
  15. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  16. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  17. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  18. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  19. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  20. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  21. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  22. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  23. </Components>
  24. <Validations>
  25. <validation rule="firstname" target="firstNameEdit"/>
  26. <validation rule="surname" target="lastNameEdit"/>
  27. </Validations>
  28. </Page>

Using data

A form like the address page can not only be used to capture data but it can also be used to edit data. Aria includes an extensive data model that allows the UI to be separated from the data so that the applications logic need not know how to get, set or maintain values in the UI. The Aria data model is also an abstract hierarchy and can be referred to with expressions. Furthermore the application does not need to know how the data is actually stored as the model will assign storage as needed. Thus the application need only specify where to store the data in the model, for example the address form can be modified to store its edit values in the model.

The figure above shows a typical situation where data is transferred from page to page without one page needing to reference the other directly. Instead the pages refer to the data in model and they are thus less dependant on one another. In the same way the application code can refer to data in the model and the code can even update the data used by the two pages. Again the code is not dependant directly upon the user interface pages.

Normally the links between the components on user interface pages are declared as data bindings.

An XML page binding data to the user-interface components

  1. <Page>
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right"
  4. style="Caption"/>
  5. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right"
  6. style="Caption"/>
  7. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right"
  8. style="Caption"/>
  9. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right"
  10. style="Caption"/>
  11. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right"
  12. style="Caption"/>
  13. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right"
  14. style="Caption"/>
  15. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  16. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  17. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  18. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  19. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  20. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  21. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  22. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  23. </Components>
  24. <Validations>
  25. <validation rule="firstname" target="firstNameEdit"/>
  26. <validation rule="surname" target="lastNameEdit"/>
  27. </Validations>
  28. <Data>
  29. <Bind target="firstNameEdit" source="customer/firstName"
  30. <Bind target="lastNameEdit" source="customer/lastName"
  31. <Bind target="phoneEdit" source="customer/phone"
  32. <Bind target="faxEdit" source="customer/fax"
  33. <Bind target="mobileEdit" source="customer/mobile"
  34. <Bind target="address1Edit" source="customer/addr1"
  35. <Bind target="address2Edit" source="customer/addr2"
  36. <Bind target="address3Edit" source="customer/addr3"
  37. </Data>
  38. </Page>

Aria will automatically update the form so that it displays the data from the model whenever the page is displayed. Aria will also save the data automatically whenever the page goes out of context.

An application has also programmable control over the model and its interaction with the UI. Bindings can be changed and can be made to dynamically point to other locations, but that will be covered in more detail in later chapters.

The data model can be primed with data by configuring a data source via the startup file. The startup file can name a datasets file (datasets.xml by default). The datasets file in turn points to individual data sources. For example datasets.xml is often configured as follows:

A sample dataset reference

  1. <DataSources>
  2. <DataSource name="InitialValues" filename="datasources.xml"/>
  3. </DataSources

The data source name doesn't have any great significance, but the filename attribute points to the actual data. For this address form example the data might appear as follows:

A sample dataset

  1. <Datasets>
  2. <dataset id="customer">
  3. <item value="John" id="firstName"/>
  4. <item value="Doe" id="lastName"/>
  5. <item value="353-1-8978677" id="phone"/>
  6. <item value="Ms" id="fax"/>
  7. <item value="Dr" id="mobile"/>
  8. <item value="Rev" id="addr1"/>
  9. <item value="Fr" id="addr2"/>
  10. <item value="Fr" id="addr2"/>
  11. </dataset>
  12. </Datasets>

This sample data will populate the address form when the form is first shown.

Responding to events

So far we have covered the UI declaration, the validations and the data model but all of that is of little benefit unless we can apply some application logic.

Aria relies on Java for its programming and at the core of this is the Page class. Each page in Aria must be derived from this class. Fortunately this is a fairly simple task.

To demonstrate how application logic can be invoked we will add a button to the form so that a new or next page can be displayed. First though we will create the stub of the page's logic code:

A simple Java class with an event handler

  1. package aria.projects.addresses;
  2.  
  3. import org.formaria.aria.*;
  4. import org.formaria.swing.*;
  5.  
  6. public class Welcome extends Page
  7. {
  8. public Welcome()
  9. {
  10. }
  11.  
  12. public void next()
  13. {
  14. }
  15. }

This class contains the stub of the next method that we will later fill out to implement the required logic, but for now we still need to connect it to the UI.

The first step in connecting the page to the new class is to change the XML page definition so that it refers to the class, thus the Page element becomes:

Referencing the Java class in the page's XML declaration

  1. <Page class="aria.projects.addresses.Welcome">

Finally we need to trigger the method in response to the button click event, or in AWT/Swing the action event. To this we add an event handler declaration:

Adding an event handler to the page's XML

  1. <event target="nextBtn" method="next" type="ActionHandler">

The complete form now appears like this:

The complete form

  1. <Page class="aria.projects.addresses.Welcome">
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right"
  4. style="Caption"/>
  5. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right"
  6. style="Caption"/>
  7. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right"
  8. style="Caption"/>
  9. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right"
  10. style="Caption"/>
  11. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right"
  12. style="Caption"/>
  13. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right"
  14. style="Caption"/>
  15. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  16. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  17. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  18. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  19. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  20. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  21. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  22. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  23. </Components>
  24. <Validations>
  25. <validation rule="firstname" target="firstNameEdit"/>
  26. <validation rule="surname" target="lastNameEdit"/>
  27. </Validations>
  28. <Data>
  29. <Bind target="firstNameEdit" source="customer/firstName"
  30. <Bind target="lastNameEdit" source="customer/lastName"
  31. <Bind target="phoneEdit" source="customer/phone"
  32. <Bind target="faxEdit" source="customer/fax"
  33. <Bind target="mobileEdit" source="customer/mobile"
  34. <Bind target="address1Edit" source="customer/addr1"
  35. <Bind target="address2Edit" source="customer/addr2"
  36. <Bind target="address3Edit" source="customer/addr3"
  37. </Data>
  38. <Events>
  39. <event target="nextBtn" method="next" type="ActionHandler">
  40. </Events>
  41. </Page>

Changing pages

One of the most common tasks in building an application is providing a means of navigating around the application and since the *Page is the basis of all pages in an Aria application the Page class provides methods for changing pages.

To show the second page we modify the 'next' method.

Implementing the event handler

  1. package aria.projects.addresses;
  2.  
  3. import org.formaria.aria.*;
  4. import org.formaria.swing.*;
  5.  
  6. public class Welcome extends Page
  7. {
  8. public Welcome()
  9. {
  10. }
  11.  
  12. public void next()
  13. {
  14. pageMgr.showPage( "pageTwo" );
  15. }
  16. }

This new code will cause the application to display a page called "pageTwo" whenever the next button is pressed. If the application was coded as above, then it would not be strictly necessarry as Aria 3.0 allows the shortcut of specifying the showPage(pageName) expressions directly within the event handler for the component in question - in this case a button. (Of course some implementation of this new page is required, but that is left as a user exercise).

The pages within an application are managed by the framework and a component on the project called the page manager. Whenever a request is made to show a page the page manager loads the page either from a Java class or by building a page from an XML file. The page is informed of its creation via its pageCreated method once all of its components have been constructed and the new page object is ready for use.

Once it has created the page the page manager requests that the applet displays the page. The page may be loaded as part of a frameset in which case the area in which it is displayed (these areas are named as part of the frameset setup). If a frameset is not being used the default target area is used, this is called the ` content ' area.

Once the page has been successfully displayed its pageActivated method is invoked.

In displaying the page the page manager first deactivates any page that is currently displayed in the new page's target area. Thus in the case of displaying page `P2' in the above figure the old page `P1' must be removed from the target area. In doing so the page manager calls the pageDeactivated method to indicate the change of state.

Projects

Aria applications are organized into projects with a well defined structure. The project acts to organize and coordinate the project and its resources. At both design and run time the project plays a crucial role in the behavior of the application. To help you better understand this behavior we have included an overview of a project and what it does for a Aria application.

A project's role

A project is a collection of the resources used in an application. The project object within a Aria application manages these resources and provides many facilities for loading and storing resources.

As many of the components in a Aria application are loosely bound to one another some coordination of these otherwise disparate resources is required. The project therefore acts as a central coordinating resource and in programming an application the project is often the first place to look when trying to reference a component of the application.

Aria projects also act as a way of ring-fencing the resources used by an application and the application context. The idea behind this is that you could have multiple projects running with a single JVM and you would therefore need to manage the resources of each project separately..

Originally Aria had used singletons (classes containing a static self reference) to provide easy access to key resource classes such as the StyleManager that one might want to access in a great variety of places. While the singleton pattern made it easy to access these resources it greatly complicated initialization and the project lifecycle, never mind making it difficult to handle more than one project at a time. Aria 2.0 did away with the singletons and now all major objects are owned by the individual projects and ultimately by the project manager. The stricter hierarchy that this imposes makes managing an application somewhat easier and makes initialization clearer

Creating a new project

A new project can be setup using the Aria or AriaEditor plug-ins by starting the New Project Wizard from the file menu. Under NetBeans the wizard creates a new project and the associated infrastructure for a normal Java application project. The new project is immediately ready for running and debugging. Similarly on Eclipse, a complet project is setup and again you can immediately run or debug the application.

The process of creating a new project involves a few steps and choices which will influence the style of the application. Most of these steps can be undone or modified at a later stage, so don't worry too much about the selected options. In most cases you can just choose the default options.

The wizard is staightforward and once the project has been created and open, the project editor shows the sames choices that were available while using the New Project Wizard. The role and use of the various options is explained more in the following paragraphs.

Project setup

A project file is the key to a project. Aria's project files are contained in the root of the project and have a .aria extension. The file is an XML file, but fortunately Aria provides an editor for all the project's settings:

The editor is displayed automatically whenever a project folder is opened. The project also manages access to a number of folders. Each folder contains a distinct type of resource. Here is a summary of those folders:

Project folders

Folder

Usage

Pages

Stores the XML page descriptions

Lang

Stores the language files used for translation of the application.

Resources

Holds various resources used in the project including graphics, configuration files and so on.

Src

The source code for the Java classes is held in this folder. Another folder (classes) may also be created depending on the configuration. The classes folder is a temporary folder and may be deleted at any time.

General setup

When the project initially opens the setup page of the project settings is displayed.

The settings on this page are general initialization and project description settings.

These settings can be configured at any time in the life of the project, but of course they may affect the runtime behavior. For example it is possible to switch widget sets but doing so may affect your business logic code if you have done something that specifically references an individual widget set. Similarly resizing a page to a smaller size may result in components not being visible to the end user.

If there is a problem you can always revert your settings to the previous values and your setup should be restored.

The properties on this page are:

General properties

Property

Usage

Project name

This is the name by which the project is identified within Aria.

Application title

The title of the application. The title is displayed on the application title bar at run-time for a normal application. Some applications do not display a title bar.

Screen size

The width and height of the page. The application frame is adjusted so that the pages are the size specified here. The drop down provides a short-cut to some common sizes and the edit fields allow more precise adjustment of the size.

Centre on screen

Check this option for the application to start-up in the centre of the primary display. Otherwise the application will start in the top left of the display.

Popup window

Check this option to use a popup window. The option can be useful for creation of full screen like applications as no border or title bar is displayed. The option may also be of use for applets where it is not intended that the applet is to appear `embedded' within the html page.

Widget set

Aria applications can run with a number of widget sets and the framework provides a certain level of isolation from the specifics of the widget set. You can use this option to choose or switch widget sets. Support for other widget sets is available outside of the editor.

Initial page name

You can select the page to display when the application starts using this option. If a frameset is being used this page will be displayed in the default content frame.

Package name

Specify the default package name for your application code here. The package name should follow the normal Java package naming conventions.

Log level

Aria provides a logging facility for you applications when they are run with debug libraries. Various levels of verbosity can be chosen. The log is often the first place to look if you encounter problems when running the application.

Frames setup

Aria supports the notion of framesets much the same way as HTML does. Frames are subdivisions of the screen or more precisely the applications client area. The frameset divides this space into several target areas with each target area containing a page. The target areas can be updated by displaying new pages within one or another.

It is entirely up to the individual application how these frames are updated. If no target area is specified when displaying a page the default content target is updated.

The content of some targets can be long running to provide application wide facilities such as navigation panels, headers and footers. An application need not employ a frameset.

The frameset is based on a border area and the editor page visually reflects this setup. Each of the areas can be named and a size can be suggested for the area. At least one target area must be included and normally this is the content target.

Files Setup

Rather than try to shoehorn everything into one configuration file Aria uses a main configuration file, the project file to direct the framework to individual configuration files for particular elements of the project. These configuration files are identified on the file page.

Each configuration file can be located by clicking on the button next to the edit field to popup the file chooser.

The list of files is not exhaustive and as we will see later there may be more configuration files needed for some projects. Most of the configuration files are optional so it is not fatal if you are missing one or another. The system will attempt to load a default if one file is missing and any difficulty is logged.

The individual files are:

File settings

File

Usage

Styles

The style file contains information about the colours and fonts used within an application much like cascading stylesheets are used for HTML. By default the style file is located in the resources folder and is called styles.xml.

Frames

As mentioned above an application can optionally use a frameset. The frameset configuration is saved to the file specified here. If no frames file is found then it is assumed that the application has a single content area called content. The default frameset file is frames.xml and it should be found in the pages folder.

Model data

Aria applications include a rich data model. The data model can be configured in a wide variety of ways with various data sources contributing to the overall model. Loading of the data model is initiated by reading of the model data configuration specified by the file name here. The default file is datasets.xml and it is normally placed in the resources folder.

Validation rules

Validation rules are rules that specify what is valid data, they constrain the user input to particular ranges of data that is at first glance acceptable to the application. The validation rules are configured via the validations file specified here.

Look and feel

The look and feel of the application only requires a choice of files if the Synth look and feel is employed.

Extensions setup

Aria offers numerous extension possibilities an some of these extensions can be configured here.

The most common extensions, adding a component factory, adding and data binding factory and adding a validation factory can all be configured here.

The add option allows you to select the class that implements the appropriate extension.

It is worth noting that Aria now includes a facility to register components via XML so you do not need to build a custom component factory to use additional components. The component factories are covered in detail in later chapters.

Extra setup

As an extensible framework it is difficult if not impossible for Aria to know all the configuration parameters in advance. Therefore we have provided a generic facility to specify project configuration parameters.

The Extra page provides a table where you can add, remove and edit project settings. These settings are persisted to the project file.

Extensions can install additional parameters and third party configuration parameters are likely to appear in this list. Aria itself uses this facility for some of its optional parameters and you will see this project editing facility referenced in later chapters.

Services provided

From a programming perspective the project provides the following services:

  • File access
  • Image loading
  • Startup parameter management
  • Resource management
  • Pages
  • Styles
  • Images
  • Data model
  • Classpaths

Aria tries to simplify access to resources and therefore the project sometime needs to search several possible locations to find an individual resource. At it simplest a Aria project normally refers to:

  • The project root,
  • The pages folder,
  • The lang folder,
  • The resources folder,
  • The Jar classpaths

The project can be accessed through the ProjectManager class which provides a static accessor:

Locating a project

  1. Project currentProject = ProjectManger.getCurrentProject();

This manual refers to the project on many occasions and this serves to highlight the pivotal role that a project has in managing a project. As mentioned above, when developing code within a Aria application the project is also often the first place to look when trying to find a particular resource.

As of Aria 2.0 the Page class includes a reference project to the current project so the above code may no longer be necessary in most situations.

Build Pages with the Page Designer

Once you have setup a project the next task is to create some content, and this means adding pages to you application. Aria includes many advanced features for editing pages, forms and other content for you rapplication, but this chapter gives a flying overview of the main features of the editor.

The role of pages

Pages are one of the main the building blocks of a Aria application and in most cases they tie in closely with the lifecycle and navigation of your applications main elements. Pages are designed to help you lead through the story (or more prosaically, the workflow) of your application, but pages are still quite flexible and not so strictly tied to the navigation that they restrict how you present your information, and later we will see how the role of pages is affected by the application style.

Creating a new page

Once a project has been created you can use the New Page Wizard to create a new page. The wizard is very simple, just taking the name of the new page as an input. If you are using the Files view within NetBeans then you can right click on the `pages' folder and choose the New Aria Page... option.

Once the new page has been created it is opened in the page designer.

Opening the page

The pages created by Aria are inserted into the pages folder, and this folder is automatically added to the project classspath. New pages are automatically opened, but existing ones can be opened by double-clicking on the page file or by choosing the Open option from the node's context menu. By default the page opens as a standalone page, but it can also be opened in the context of the pages named by the frameset, by choosing the Open in frameset command.

On Eclipse the behavior varies slightly and initially the default action when double clicking a page may be to open the page's XML in the default text editor, however once the page has first been opened subsequent double clicks will open the Aria page designer. Eclipse users may also have to open the AriaEditor perspective before all features of the page designer are available (see the Window|Show View|Other option).

Pages must belong to a project and normally a page can only exist or operate within the context of its project as the project provides access to the resources used by the page. Therefore to successfully use a page the owning project must first be opened, and to ensure this relationship is maintained Aria will open the project whenever you first attempt to open a page within a project.

For an overview of the main elements of the page designer please see .

Opening the page in a frameset

A page can also be opened as an element of the frameset (the content/center element) by choosing the Open in frameset command from the page node's context menu. When the page is opened in this way the page will be viewed as though the frameset devides the main window into named target areas that can be controller by the page manager. This editing mode assumes that the frameset is being used as part of an SDI (Single Document Interface) application even though the frameset can be used for other application styles.

Can should be taken with this editing mode it the pages referenced and opened by the framset are already open in other page designer windows. There will be a single instance of any page edited within the page designer and if the same page is opened in multple instances of the page designer then the changes made in one instance will affect the others (in fact the page being edited is just shown in the other instance of the designer once it becomes active).

The image below shows the Welcome page being as part of the frameset, in this case in the center of the frameset.

Preferred page size

If a page is bein used within a frameset it is not tied to any particular target area within the frameset and the application can instead chose to show it in any area. However, in practice the page will usually be shown in specific area and at a specific size. Some layouts can accomodate radical resizing depending on the content but for many it is useful to know the size that will be used for the default layout. By right clicking on a page's node you can choose the Preferred page size... option and set the default size for the component.

The dialog allows you to choose the area to use for the page based on the areas assigned by the frameset. Note that the application area is normally larger than the sum of the sizes of the pages held within the frameset as the application also includes decorations like borders and headers. Also not that even including the size consumed by headers and borders there may be some deviation if the application's end user has setup their computer with larger or smaller than normal fonts as default (this affects the header size) or tweak the operating system's user interface in a variety of ways. Using layout managers rather than absolute layout will help overcome the poblems caused by such differences between systems.

Adding components

Once the page has been opened for editing, adding components is straightforward. The component palette at the top right contains icons for most of the components that can be added to your application. If Aria is installed you will see two pages of components (two tabs) and you can also register new components or third party components for use in Aria (see See Working with components.).

To add a component, just click on the icon corresponding to the component you want to add, move the mouse to the point on the page where you want to insert the new component and click the mouse. The new component should be inserted at this point. If you do not recognise the icon for a component just let the mouse hover over the icon for a moment and you should see a tooltip telling you what type of component is represented by the icon.

If you have already added components to the page you may also be able to add component as children of existing components. Some components like panels and scrollpanes act as containers for other components, so to add a child to such a component just click on the container into which you want to insert your new component instead of the page.

Selecting and sizing a component

A component can be selected by clicking on the component, and multiple components can be selected by holding down the CTRL key while clicking on the components. (Only the common properties will be displayed in the property sheet)

A selected component (or components) can be moved by dragging it to another part of the page. The selected component can be sized by grabbing and dragging one of the selection handles.

The movement and positioning of the components will be affected by the grid and snapping properties of the page designer. You can access these propeties by right clicking on the page and selecting the appropriate options from the context menu.

If any of the snapping options are selected the components will snap to the appropriate positions so as to match the snap options. The arrow keys can also be used to move the components and moving the components with the arrow keys also honours the snapping properties of the page designer.

If the arrow keys are being used to manipulate the components holding down the CTRL key while using the keys cause the component size to vary instead of the position.

Sometimes the snapping options will make the components seem to stick to a particular position. A short sharp movement usually frees the component, but as an alternative you can type the coordinates or sizes directly into the component's property sheet to manipulate its position and size.

Equally, when dragging a component the size of the component may vary so that its edges honour the page designer's alignment properties, but when the component goes out of range of the snapping zone its size should be restored.

Nudging a component

A selected component can be nudged or moved using the arrow keys. Movements with the arrow keys are still influenced by the snap settings, the grid options and the layout managers and therefore using the arrow keys to nudge a component will lead to its size and positions being updated which can be desireable when properties have been changed via the property sheets.

Aligning components

The page designer properties also include the option to have components Snap to siblings so that components align with one another. When using this option and dragging components about the form alignment hints are briefly shown to indicate how one edge aligns, or snaps to another. When aligning edges the page designer attempts to allow for the operating system's recommendations for component spacing.

Setting up guides

Supplementing the snapping options are guides, which can either enforce layout considerations or merely act as a layout aide. Guides can be setup interactively once the designer has been placed into the edit mode, by clicking the button at the junction of the horizontal and vertical rulers. When in the mode the rulers change color.

In the (guide) edit mode, clicking on a ruler and dragging the mouse onto the page will add a new guide to the page. Equally a guide can be deleted by dragging it back to the ruler.

Further setup options for the guides can be configured by right clicking an individual guide and choosing Guide options... There options are covered in more detail in later sections (see See Guides.). You can also add columns and rows for regular form layouts by choosing the Setup columns... option.

Once the guides have been added you can select the Snap to guides option so that moving the components will snap them to the nearest guide within the snap zone. Setting this mode is not necessary if you wish to use the guides as a visual indication only. To exit the guide edit mode, again click on the button ath the junction of the rulers.

Layout managers and constraints

The final factor influencing the layout of components is the use of layout managers. Layout managers automatically adjust the size and position of a component when its parent has been resized. Each container (page or panel etc...) can have a layout manager, set via the property sheet for that container, and in turn each component can have layout constraints should the parent use constraints (some don't). For more information on how layout managers are used see See Layout..

In the context of the above overview of moving and sizing a component it should be noted that a layout manager will trump any of the above features and layout the component as it sees fit. Of course Aria's own layout managers may respect the positioning defined with the page designer, but this behavior varies from layout manager to layout manager.

Setting properties

When a component is selected the properties of that component are shown in a property sheet to the bottom right of the page designer.

Once the component properties are listed in the property sheet they can be set by clicking on the property value. The property can then be edited. Some properties will display a list of appropriate values from which you can select, whereas others will allow you freely enter a value of your choice. Editing of a property is normally completed by pressing the enter key, or by selecting another property or component. Once the new property has been entered the component is updated.

In-situ editing

For conveniece, a property sheet can be shown next to the component under the cursor. This mode of editing properties is known as in-situ editing. The in-situ editing mode can be changed by toggling the mode using the toolbar toggle button. The mode can be turned on and off, or a full property sheet, a cut-down mode can be shown.

Just as with the property sheet you click on a property value to edit that value. The in-situ editor follows the cursor after a short delay, so some care may be needed when moving the cursor from the component to the in-situ property sheet, but after some experimentation it should save you some mouse clicks and cursor movement.

Normally the page properties do not show in the in situ editor as they would constantly get in the way of normal editing, but the page properties can be displayed in the in situ editor by moving the cursor to the top left corner of the page while holding down the CTRL key.

Similarly, the in situ editor normally masks components beneath the editor to avoid unintentionally selecting other components while trying to edit a components properties. However sometimes it is desireable to select a component that is obscured by the in situ editor, and to do so hold the CTRL key down while moving the cursor.

Quickly setting the content

Another useful labour saving shortcut is to double click a component so that the editor for the default property is displayed. Typically the default property controls the content or text displayed in the component, or else the component name.

Styling a component

Components can be styled in a number of ways and depending upon the individual component there may be one or more styles. Like any other property the styles can be set using the property sheet or with the in-situ property sheet. AriaEditor also includes a style palette, located in the same area as the property sheet (but on the tab marked `Styles' ).

To set the style using the style palette select a component or components and double click a style within the style palette. The styles (mostly the colours and fonts) associated with the style are applied to the component. If the style is changed at some later stage then the component will be updated to reflected the modified style.

New styls can be configured by right clicking on a style in the style palette.

Delete, Cut, Copy and Paste

You can delete, cut, copy and paste components using the normal keyboad shortcuts, and by right clicking and choosing from the context menu. When components are cut and pasted they can be inserted multiple times. The insertion point is at the cursor position.

As the components are added they are renamed to avoid a name collision. Components can also be cut and pasted between panels and pages.

Locking components

Components can be locked so that they are not moved or modified accidentally. To lock a component right click on the component and choose lock from the context menu. The component can also be locked by choosing the component within the Component Inspector and using the inspector's context menu.

When a component is locked using the Component Inspector its children are also locked, and therefore a combination of the two context menus can help you quicked toggle the locking status of a hierarchy of components.

Hidden components

Components can be shown or hidden usisng the Visible property via the property sheets. Of course an invisible component cannot be selected in the normal way within the page designer, however the component will still be listed in the component inspector and selected via the component inspector, and once selected the component properties can be changed via the proeprty sheet.

Hooking up code

A page layout without code and functionality is little use and therefore the property sheet lists the events that can be associated with the selected component.

Adding an event handler is simply a matter of inserting the name of the event's response handler method in the property sheet. Once the value has been entered, the Java source code for the handler is inserted. Under the hood, the Aria framework takes care of the event handling plumbing.

In some cases you may not want to add a new event handler, so instead you can select the existing methods as the event handler from a list of methods shown in the property sheet.

Inserting predefined code

Within the source code editor you have access to all the normal code templates available within the IDE (be that NetBeans or Eclipse). In addition to the templates the source code editor's context menu include the Run Aria helper... option. Choosing this option pops up a dialog that lists some common Aria code fragments (this list can be extended).

Working with data

Aria supports many data formats and data source (including customized datasources) and the Aria plug-in allows for easy setup of the data sources and the data bindings that will be used to channel data into the on screen components.

Setting up a database table

Within NetBeans the Runtime window allows access to servers and other runtime resources. To setup a database within the Runtime view, first open the view from the Window menu and right click the Databases node. Add a database, following the instructions provided by the IDE. Even if you intend to use a standalone database like HSQLDB it is worth setting the database in server mode so that you can make best use of the tools provided by the IDE.

Once the database server has been setup choose the Aria | Visualization | Show the data visualizer view to open the Data Visualizer . Once open you can drag and drop tables from the Runtime | Database to the Data Visualizer to register the table and configure it for use in standalone application. A project must be open before the visualizer can be opened.

Setting up POJOs

TODO insert screen shots of setting a data binding

Setting up static data

For some nodes you can configure the data model as you go within the visualizer (you can also do this at runtime). The data visualizer includes toolbar buttons to allow you select the paths and to add or delete nodes. See Data binding. for more detailed information on using and managing data bindings.

Binding

Once data is available in the visualizer you can use it in multiple ways. The simplest way of setting up a binding is to drag and drop a node to the component to which it will be bound. Using drag and drop in this way fully configures the bindings.

For some types of nodes and some types of data a popup dialog may appear requesting further specification of the data binding attributes, for instance specifying the field to display in a drop down list or combo box.

Alternatively you can copy and paste the data path so as to later insert it in a data binding specification or path.

TODO insert screen shots of setting a data source

TODO insert screen shots of setting a data binding

Working with validations

TODO insert screen shots of setting a data source

And now for the XML

At times you may wish to work with XML directly, or use features that cannot be coded directly with the page designer and therefore the XML is just a click away. The page designer's toolbar has a button marked XML , which when clicked shows the XML editor.

You can switch back and forth between the XML and the Design view of the page designer at any time and the views are updated in the process.

Working with included pages

An advanced feature of Aria, that we will see more of in later chapters, is that one page can be included in another. The page designer offers some support for this, allowing you view and edit pages that have been composed in this way, however the editor does not yet allow you to choose which page a new component will be belong to. Instead the page designer uses the component hierarchy to decide what components belong to what page. Therefore the children of a component such as a panel belong to an included panel will themselves belong to the included panel.

Saving

Finally, your edits need to be saved and this can be done on a page by page basis using the normal File | Save menu action, or all edits can be saved at once using the File | Save All menu action.

Modified pages or resources are shown in the Files or Project view with a red dot or with an asterisk. When saved the dot/asterisk should disappear.

Building Pages with XML

As we have already seen, Aria allows pages to be built without having to write Java code to setup the user interface components. The functionality afforded by this process is not complex and contributes greatly towards the goal of removing trivial screen building from your Java code.

Pages built with Aria can be integrated with pages coded directly in Java and with Java classes containing just your application logic. Very little change is needed to the application to employ this package, in fact all you need do is use the XML page names when making requests to show a page.

The next few sections provide an overview of what can be done with the XML declarations. Later chapters go into greater detail about specific features such as validation and data binding.

Basic form building

The basic components and layout of a user interface can be described in an external XML. The XML file contains entries for the individual components on the page which are processed by the Aria framework when it constructs a page.

You can think of a page as panel or container that holds your user interface components. The page is constructed from either a Java class or from an XML file by the page manager component built into the Aria framework.

In processing the XML the framework constructs a page object and adds the specified components to it. The page and the objects added are all normal Java components so once the page has been setup you can interact with all the components as though the page had been constructed with Java code.

A snippet of the XML file for the form shown above is shown below.

A basic address page described in XML

  1. <?xml version="1.0" encoding="UTF-8"?></P>
  2. <P>
  3. <Page>
  4. <Components>
  5. <Panel x="10" y="10" w="210" h="80" border="1">
  6. <Label x="5" y="10" w="90" h="20" content="Firstname:" style="prompt"/>
  7. <Edit x="100" y="10" w="100" h="20" content="Joe"/>
  8. <Label x="5" y="30" w="90" h="20" content="Surname:" style="prompt"/>
  9. <Edit x="100" y="30" w="100" h="20" content="Bloggs"/>
  10. <Label x="5" y="50" w="90" h="20" content="Age:" style="prompt"/>
  11. <Edit x="100" y="50" w="100" h="20" content="38"/>
  12. </Panel>
  13. </Components>
  14. </Page>

Perhaps the most important thing to note about this sample is that there is absolutely no Java code written in order to display it.

This absence of a Java class leads to the important question of how it is possible for a page to be both and XML file and a Java class. The answer of course is that it isn't. Aria constructs an instance of a Java class, the Page class by default and populates it with the components you have specified in the XML.

Indeed, you can even customize this page class to add your own functionality and behavior by specifying the class to create and populate (as in the example below where an instance of QuestionPage is constructed).

Much can be achieved with this simple step of putting the user interface declaration into XML. Some additional processing and aggregation of content can be performed, for example by adding a new XML element it is possible to allows for repeated inclusion of children.

Thus one page can be composed of multiple pages.

An advanced page decription showing repeating elements, embedded data etc

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page next="Finished" layout="Border" class="org.formaria.xforms.awt.QuestionPage">
  3. <Components>
  4. <Panel layout="Box" layoutStyle="0" constraint="center">
  5. <Repeat while="hasMoreQuestions()">
  6. <Include file="template"/>
  7. </Repeat>
  8. </Panel>
  9. </Components>

This aggregation further extends the separation of UI from the data model by allowing multiple instances of a component or set of components to be included. The data or content displayed by these components can be even pulled from a data model on demand.

Built-in components

Aria includes a set of built-in components that can be used interchangeably in Swing or AWT applications. These components are listed below along with a brief description. The XML element name is shown in brackets[]. Note that this name is not case sensitive.

Component types

Component

Usage

Button

A simple push button type. [ Button ]

Checkbox

A check box normally used to indicate options [ Checkbox ]

ComboBox

A drop down list used to display a fixed set of choices or options.[ Combo ]

Dialog

A holder for a page that is displayed in a popup window or dialog. The dialog can be modal or modeless. [requires a separate page to be shown, the dialog is actually constructed implicitly when the showDialog method is used]

Edit

A basic input field. [ Edit ]

HotspotImage

An image that when clicked in certain areas triggers events. [ Hotspot ]

Image

A simple image display component. [ Image ]

ImageMap

An extension of XImage that allows querying of the mouse coordinates. [ ImageMap ]

Label

A simple static piece of text that does not permit user interaction. Text can be flowed over multiple lines. [ Label ]

List

A simple list. [ List ]

Menu

A menu, either drop down or popup, depending on the context. [ Menu ]

MenuBar

A menu bar for holding menus in an application. [ MenuBar ]

MenuItem

An individual menu item. Selection triggers an action. [ MenuItem ]

MessageBox

A popup dialog for display of simple messages, warnings or errors. [constructed implicitly by calling showMessage ]

MetaContent

A text field that can display some simple meta content, altering typefaces and embedding tables and so on. [ MetaContent ]

Panel

A container that can holder other components. The container can be displayed with a border and is considered to 'own' its children. [ Panel ]

Password

An edit field that masks the user input so that an observer can not see what has been keyed. [ Password ]

RadioButton

A radio button control for selection of mutually exclusive options. [ RadioButton ].

Note that when a set of radio buttons appear one after the other in XML they will automatically be added to the same radio button group. The group will be reset when any other type of component is encountered.

ScrollPane

A container that allows scrolling of its content. [ ScrollPane ]

ScrollableMetaContent

A version of the meta content control that supports scrolling of its content. [ ScrollabelMetaContent ]

Tab

An individual Tab for a tab pane. [Added implicitly when a child panel is added to a tab panel]

Table

A component that displays tabular data. [ Table ]

TabPanel

A contain that allows access to multiple child panels by selection of a tab. [ TabPanel ]

TextArea

A multiline text field. [TextArea]

More details of these controls can be found in the appendix.

The component can be used by creating an child element of the ' Components ' element or one of its children (a panel for instance). The new element should begin with the name of the component type (as listed above in parenthesis), for example a new button is described as

Specifying a button

 <Button name="myButton" x="10" y="10" w="100" h="25"/>

The actual attributes depend on the component type.

Common properties

Although the full set of attributes for a component are specific to that component, a subset of these attributes are shared by all components. These attributes are optional and need not be specified in all cases.

The attribute names in the XML are case insensitive, but generally they are specified in lower case. Some of the attribute properties are used for constant values, for example when specifying alignment term like `left' and `right' are used. In such cases the attribute values are also case insensitive.

Common component properties

Property

Usage

name

The component name as it will be used for lookups. If you intend interacting with a component it is a good idea to name the component. Failing this an index can be used to access the component and this depends on the order in which components were added.

x

The 'x' or left coordinate of the component (if absolute positioning is being used).

y

The 'y' or top coordinate of the component (if absolute positioning is being used).

w

The width dimension of the component (if absolute positioning is being used).

h

The height dimension of the component (if absolute positioning is being used).

style

The component style name, referencing the style file. This will set the colors and font of the component

content

The component content depending on the component type. Frequently this refers to the text displayed by the component.

Panels

Panels differ from other components in that they are designed to contain other components. The ' panel ' element needs to span the children to conform to normal XML document structure e.g.

A panel

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page>
  3. <Components>
  4. <Panel layout="Box" layoutStyle="0" constraint="center" border="1">
  5. <Labelx="5" y="10" w="90" h="20" content="Firstname:" style="prompt"/>
  6. <Edit x="100" y="10" w="100" h="20" content="Joe"/>
  7. </Panel>
  8. </Components>

Panels may also specify a border attribute so as to display a thin line border around the edges of the panel. The value for the border attribute should be '1' for a border or '0' for none or else the attribute can be omitted.

The panel is considered to own its children so showing or hiding a panel will also show or hide its children.

A panel can include a layout manager by specifying the 'layout' attribute. In the above example the layout is set to the ' Box ' layout type. If a layout is being used each of its children should be given a 'constraint' attribute.

ScrollPanes

Scrollpanes have many of the same properties as panels, however they implicitly have associated layout managers. These layout managers will in some circumstances resize the children of the scrollpane, for example where tables are being used.

Layout managers are not always necessary with the scrollpanes and in such circumstances you will need to appropriately size the scrollpane and its content.

Frames

Frameset support is included to allow for inclusion of non swapping areas of the screen such as toolbars, navigation controls, headers and sidebars. Each frame contains an Page and is positioned using a layout manager. Navigation commands such showPage have been enhanced to take the name of the target frame.

A frameset

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <FrameSet>
  3. <Frame name="header" constraint="NORTH" width="640" height="80" content="header"/>
  4. <Frame name="content" constraint="CENTER" width="640" height="350" content="Welcome"/>
  5. <Frame name="footer" constraint="SOUTH" width="640" height="50" content="footer"/>
  6. </FrameSet>

By default the frames are described in the frames.xml file an example of which is shown below.

The container for the frames or target areas has a BorderLayout .

Using imported pages

As mentioned earlier pages can be imported into others by using the import tag in the XML file. Importing pages allows reuse of common page elements and also helps promote consistency within the user interface.

In this example we're importing a simple navigation screen in two other files. The imported file defines two buttons within a panel as follows

The included file

  1. <Component type="PANEL" x="10" y="180" w="210" h="40" border="1">
  2. <Button name="btnProceed" x="40" y="10" w="60" h="20" content="Next"/>
  3. <Button name="btnPrevious" x="110" y="10" w="60" h="20" content="Previous"
  4. style="btnReverse"/>
  5. </Component>

Now, in order to use this screen we simply use the Include tag within another screen.

The include statement

 <Include file="xmlimportnav">

And to attach events to the buttons we add events for each screen.

Attaching an event

  1. <Events>
  2. <Event name="nextPage" target="btnProceed" type="MouseHandler"
  3. next="xmlimportsamplescr2"/>
  4. </Events>

Aria provides support for the Include element so that it can be nested within the Components element or its children. An example of this can be seen in conjunction with the Repeat element.

The declarations within the included file are processed as though they are part of the page within which they are included. Therefore an include `page' does not actually cause one page to be instantiated within another.

Repeat elements

The Repeat element includes a while attribute that specifies the criteria for repetition. In the example above a method returning a boolean value is invoked.

The features supports component, event and data binding elements. Generally the repeat attribute is used in conjunction with the include element so that common page fragments can be reused and included multiple time within a page.

In the example below the repeat attribute is used in this way to repeatedly include a radio button component.

The complete example

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page>
  3. <Components>
  4. <Panel layout="Border" style="question" w="640" h="75" constraint="center">
  5. <Panel layout="Flow" style="question" constraint="north" >
  6. <Image name="icon" w="20" h="20" content="qmark.gif" style="Question"/>
  7. <Label name="qtext" w="620" h="20" content="getNextQuestion()" style="Question"/>
  8. </Panel>
  9. <Panel layout="Box" layoutStyle="1" constraint="center" w="630" h="55">
  10. <Repeat while="${hasMoreOptions()}">
  11. <RadioButton w="100" h="20" content="${getNextOption()}" name="${getOptionId()}"
  12. style="question/response"/>
  13. </Repeat>
  14. </Panel>
  15. </Panel>
  16. </Components>

The example is intended to be embedded in another page via an include element and therefore the method calls are implemented by the parent page's class.

Layout managers

Layout managers can be used in Aria via both Java code and XML. In the Java API a new method has been added to the ComponentFactory class to allow components to be constructed with a layout manager as an alternative to the absolute coordinates.

The new method takes one parameter, the layout manager constraint.

Adding a component via Java (API)

 public Component addComponent( int type, Object constraint );

The constraint depends on the type of layout manager being used, so for example for a BorderLayout the constraints are BorderLayout.NORTH, BorderLayout.WEST and so on. However before using this method of adding components the LayoutManager needs to be set. Again this can be accomplished with the component factory by calling the method within Java

Specifying a layout (API)

 public LayoutManager addLayout( layoutType );

The layoutType can be one of the Page constants:

Layout types

Type

Behavior

Border

Aligns components in five zone, along the edges (North, South, East and West) of the container or in the centre of the container.

Flow

Aligns components horizontally or vertically in the order in which they are added.

Card

Fills the container with one of the components, like a rolodeck.

Grid

A tabular type of layout where components can be placed in a grid cell in the order in which they are added

GridBag

A complex layout. No support is given within the XuiEditor

Box

Arrange components in a row or column.

Scale

Reposition the components relative to the container size and in proportion to the original container size and absolute coordinates. A variation on the null layout.

Spring

A complex layout, not yet supported in the XuiEditor.

Guide

The XuiEditor preferred layout. Components are aligned along the visual guides set out in the editor.

null

No layout. Use absolute positioning.

In the XML page descriptions layout managers can be specified for both pages and panels. An example is shown below. For both the Page element and Panel components two attributes can be used: ' layout ' and ' layoutStyle '. The layout attribute specifies the layout type ( Border, Flow, Card, Grid or GridBag ) and is case insensitive. If the attribute is omitted a NULL layout is assumed. The layout attribute ties into a set of attributes in the applications style file.

The components are then added using a constraint attribute instead of the x,y,w and h attributes of the NULL layout. The attribute strings are converted to constants via the LayoutHelper class.

A simple border layout

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page layout="BORDER">
  3. <Components>
  4. <Button name="northBtn" constraint="NORTH" content="NORTH"/>
  5. <Button name="westBtn" constraint="WEST" content="WEST"/>
  6. <Button name="eastBtn" constraint="EAST" content="EAST"/>
  7. <Button name="southBtn" constraint="SOUTH" content="SOUTH"/>
  8. <Button name="centerBtn" constraint="CENTER" content="CENTER"/>
  9. </Components>

Some layout managers (such as the FlowLayout ) rely on the preferred size of a component to calculate the layout. In such cases a W and H attribute can be specified to set the components preferred and minimum size.

More details on the layouts can be found in the chapter See Layout..

Validations

Validations are rules that can be used to check that input is correct. Aria includes a number of built-in validation types, but since the validation of input can range from a trivial to a complex task the framework for performing validations is extensible. Ultimately you can add your own input validation rules and checks to an application.

Validations often have a general role within an application. For example the validation for a phone number or address could be of use in many places and in many applications. Therefore validation rules are specified in a separate XML file (named validations.xml by default) so that they can be used across projects. An example of the file's contents is:

A validation rules file

  1. <Validations>
  2. <validation name="firstNameRule" type="mandatory"
  3. msg="The [firstname] cannot be blank" mandatory="true"/>
  4. <validation name="surnameRule" type="mandatory"
  5. msg="The [surname] cannot be blank" mandatory="true"/>
  6. <validation name="ageRule" type="minmax" min="18" max="100"
  7. msg="The [age] ({value} [years]) must be between {min} [years] and {max} [years]"
  8. mandatory="true"/>
  9. </Validations>

The validation rules include a warning or error message that is displayed should the validation fail, i.e. if the user input is not valid. The message can include references to the parameters used to control the validation rule, the example shows the user value being embedded in the warning message as the {value} tag.

Details of the tags that can be used and the validations rules can be found in the reference documentation.

Three validation rules are specified for the example page shown below. The validations are then assigned to the input fields in the page XML. The complete XML for the form is as follows:

A complete example

  1. <Component type="PANEL" x="10" y="180" w="210" h="40" border="1">
  2. <Label x="10" y="10" w="150" h="20" content="First Name"/>
  3. <Edit name="firstNameEdit" x="170" y="10" w="100" h="20"/>
  4.  
  5. <Label x="10" y="40" w="150" h="20" content="First Name"/>
  6. <Edit name="secondNameEdit" x="170" y="40" w="100" h="20"/>
  7.  
  8. <Label x="10" y="70" w="150" h="20" content="First Name"/>
  9. <Edit name="ageEdit" x="170" y="70" w="100" h="20"/>
  10.  
  11. <Button name="validateBtn" x="10" y="130" w="150" h="20"/>
  12. </Component>
  13. <Validations>
  14. <Validation name="firstNameEdit" rule="firstNameRule" />
  15. <Validation name="secondNameEdit" rule="surnameRule" />
  16. <Validation name="ageEdit" rule="ageRule" />
  17. </Validations>
  18. <Events>
  19. <Event target="validateBtn" method="checkValidations"/>
  20. </Events>

The checkValidations method can of course be overloaded (or another method can be invoked) to do whatever custom processing is desired.

Normally it is not necessary to add such an additional check of the validations as the validations are checked upon page transition. The default behavior is to display a warning or error (for mandatory validations) message, taking the error message from the validations file and substituting values as appropriate.

Events

Under the hood Aria uses the normal Java mechanism of adding events listeners to the components on your page, however all the details of adding the events are hidden within the framework. All you need do is write a response method implementing your business logic and then tell the framework when you want that method called.

Once your XML page has been loaded by the Aria framework it exists as Java class. By changing this class you can add your own functionality and behavior to a page, even customizing the event handling for components.

To illustrate the event handling mechanism at work we need to re-introduce some Java code. First off we need to create a place in which to insert our Java code and we do this by setting the class attribute for a page. The framework that loads your page expects an instance of the Page class or one of its derivatives so we need to extend that base class.

In the example below our own custom class is derived from Page so that calls specified in the XML can be intercepted by the custom class.

Specifying a support class/page

 public class EventHandling extends Page

The XML file now specifies this custom class as its base class and defines the two buttons which have attached events.

Binding to a class

  1. <Page class="org.formaria.samples.xmlbuild.EventHandling">
  2. <Components>
  3. <Button name="btnProceed" x="40" y="10" w="60" h="20" content="Proceed"/>
  4. <Button name="btnCancel" x="110" y="10" w="60" h="20" content="Cancel"/>
  5. </Components>
  6. <Events>
  7. <Event name="nextPage" target="btnProceed" method="nextPage"
  8. type="ActionHandler" next="xmlnavsamplescr2"/>
  9. <Event name="cancel" target="btnCancel" method="cancel" type="ActionHandler"/>
  10. </Events>
  11. </Page>

So now our Java code needs to handle the events by implementing the referenced methods:

Coding event handlers

  1. public void nextPage()
  2. {
  3. // Do something here...
  4. PageManager.showPage( getAttribute( "next" ) );
  5. }
  6.  
  7. public void cancel()
  8. {
  9. Button button = ( Button ) findComponent( "btnCancel" );
  10. button.setText( "Clicked!" );
  11. }

On execution the Aria framework intercepts the action events (having been told to add an action listener for each button) and invokes the nextPage method when the btnProceed button is clicked, or the cancel method for a click on the btnCancel button.

All the plumbing needed to hook up the events and the response methods are hidden for your convenience and to make the code clearer. The hiding of the plumbing also helps reduce your code dependence of the base class. In later chapters (See Evaluated attributes and helpers.) we will see how to break free of this dependency altogether.

In some rare circumstances you may want to have more control or information about the events so the Aria framework allows you to retrieve the current event object (the event that caused your method to be invoked), you can do this by calling the getCurrentEvent() method of the base class.

Data bindings

Aria uses a data binding technique that allows components to reference data through links that point to the data's location within a hierarchical model. The data contained within this model can be simple scalar values or more complex types like lists and tables.

The bindings that link the user interface components to the data in the model take care of reading the data and updating the user interface (UI) component. The bindings can also save data to the model whenever the value stored within the user interface component changes.

Since the model lives for the duration of the application the same data model data (or nodes) can be used in many different places. For example the data from one page can be saved to the model and then reused on another page.

To establish a link between a user interface component and a node with the data model all that needs to be known is where the data resides in that model. Once the location has been declared Aria takes care of getting and saving the data.

An example of this is shown below:

Adding data

  1. <Data>
  2. <Bind target="firstNameEdit" source="defaultData/first" />
  3. <Bind target="secondNameEdit" source="defaultData/second" />
  4. </Data>

Here the data from two edit fields is persisted to two separate nodes within the data model. If the same binding specifications were inserted in another page then the edit fields to which the data was bound would update automatically when the new page was displayed.

The data model can also be accessed directly from your Java code and therefore you can use the same data within your event handlers or business logic. It is worth noting that once your data has been saved to the model it is independent of the user interface component from which it originated.

This data independence has a number of fringe benefits. Firstly your business logic can be free of dependencies on the user interface and therefore the user interface can be modified in appearance and form without affecting the workings of your business logic. In this way you could rebrand an application or refactor the user interface, even changing the type of component used to capture the user input.

Secondly, because your application code references the data through a relatively abstract mechanism it is not explicitly aware of the data source. Therefore the data source can be changed independently of the UI and of the application logic. During the life of an application this can occur at several levels. At design time some dummy data may be assembled in text files and then migrated to database tables as the application grows and ultimately the database may then be accessed remotely as the application is finally deployed. Such changes can be accommodated seamlessly by the data binding framework.

Building Pages with Java

Building pages with Java

Building a Aria application with Java is a little more complicated than using XML by its very nature however if the same sort of steps are followed in constructing the code then a simple UI application will result.

Java has many advantages over XML and notable amongst these is the reusability engendered by the object oriented nature of the language. Pages created in XML can be reused and extended (they can even be extended with XML). While an XML page is processed once as the page is loaded a Java page can have an extended lifecycle and hence a more extensive setup process. However, that said, Java and XML can be used in unison, mixing and matching as appropriate for your application.

Many of the steps and techniques used in the last chapter will be reused in this chapter, so even if you are not using XML for page declarations we would advise your to read the preceeding chapter for background information. The steps involved are much the same and differ only in implementation. Since the native Java objects are being used directly instead of via XML more features will be available, but for the purposes of the simple examples being used few of these additional features are required. As with other introductory chapters later chapters will cover the features employed in more detail.

Basic form building

At its very simplest a Aria-Java page requires a class derived from Page , the basis of all Aria pages. The traditional HelloWorld example can thus be coded:

Hello World coded in Java

  1.  
  2. import org.formaria.aria.*;
  3.  
  4. public class HelloWorld extends Page
  5. {
  6. public HelloWorld()
  7. {
  8. componentFactory.addComponent( LABEL, 10, 40, 90, 20, "Hello World" );
  9. }
  10. }
  11.  

In the above example the page's base class, Page , has defined a component factory to help construct components. The addComponent method of this factory is used to create a new component at the specified position, add it to the page, and sets its content. The constant ' LABEL ' is also defined by Page as a convenience to help identify the component type consistently. Constants are defined for all the built in types, other types are identified by name (a string value).

The use of the factory saves several method calls that are normally used when an IDE generates the UI. The more compact form produced makes it easier to see what is going on and in the long run it is thus easier to maintain.

Using stylesheets

Stylesheets specify sets of colors and fonts. These styles are arranged hierarchically and are intended to add consistency to an application's look. Using styles with the component factory also saves some more setup code.

Like many of the components in Aria the styles are defined in a styles file. The startup file should include a setting for a ' StyleFile ' which points to the styles file.

Startup Properties including a style file reference

  1. UseWindow=false
  2. ClientWidth=240
  3. ClientHeight=300
  4. StartPackage=org.formaria.samples.simple
  5. StartClass=StyleSheet
  6. Title=Simple Form Sample
  7. StyleFile=stylesheetstyle.xml

To use the styles the component factory addComponent method signature has been extended to take an extra parameter, the name of the style. The Label components might all use the explicit style 'prompt', whereas the Edit components are styled implicitly by including the Edit style (if it exists) in the stylesheet. In fact each component has a default style name based upon its type name, but you can override these defaults and specify the style explicitly by appending the style name to the addComponent method call. The fragment below shows two such component additions.

Adding a label and an edit field

  1. componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Firstname:", "prompt" );
  2. txtFirstname = (XEdit)componentFactory.addComponent( EDIT, 100, 10, 100, 20, "Joe" );

The construction of the buttons shows the explicit and implicit use of styles. Following on from this the ' Proceed ' button has the XButton style applied because it does not specify a style whereas the ' Cancel ' has specified it's own style.

Adding OK and Cancel buttons

  1. btnOK = (Button)componentFactory.addComponent( BUTTON, 40, 10, 60, 20, "Proceed" );
  2. btnCancel = (Button)componentFactory.addComponent( BUTTON, 110,10,60, 20,
  3. "Cancel", "BtnReverse" )

The result of this is shown below. What you see should be identical to what you would have seen had the page been constructed via XML:

Event handling

Using the same page as in the previous section we can see how events are hooked up easily to components without the need to add listeners or import the usual event packages.

Events are added by calling the addSomeEvtHandler of the Page, where SomeEvt is the event type, for example Item , Mouse , Action and so on. These calls take two parameters, the first being the component which we want to attach the event to and the second being the method to call when the event is triggered.

Adding event handlers

  1. private void addEvents()
  2. {
  3. addTextHandler( txtCarCost, "changeAdvance" );
  4. addTextHandler( txtDeposit, "changeAdvance" );
  5. addMouseHandler( btnOK, "proceed" );
  6. }

The called methods must be declared as public void .

Simple event handlers

  1. public void changeAdvance()
  2. {
  3. float cost = Float.parseFloat( txtCarCost.getText() );
  4. float deposit = Float.parseFloat( txtDeposit.getText() );
  5. txtAdvance.setText( String.valueOf( cost-deposit) );
  6. }
  7.  
  8. public void proceed()
  9. {
  10. if ( wasMouseClicked() )
  11. btnOK.setLabel( "Clicked!" );
  12. }

The example illustrates a common error in adding events. In the implementation of the proceed method above you can see that we can use the Page.wasMouseClicked() method to determine if it was a `click' which triggered the event. The mouse handler is invoked for multiple events like the mouse press, mouse move and mouse release events and we do not want to respond to all of those events.

The problem is that although we may be responding to a mouse click, the appropriate handler for a button click is in fact an action handler. Therefore instead of using the addMouseHandler the addActionHandler should have been used. Correcting this error the declaration now looks like:

Revised addition of event handlers

  1. private void addEvents()
  2. {
  3. addTextHandler( txtCarCost, "changeAdvance" );
  4. addTextHandler( txtDeposit, "changeAdvance" );
  5. addActionHandler( btnOK, "proceed" );
  6. }

With this revision we can simplify the response method to remove the unnecessary checks.

Revised simple event handlers

  1. public void changeAdvance()
  2. {
  3. float cost = Float.parseFloat( txtCarCost.getText() );
  4. float deposit = Float.parseFloat( txtDeposit.getText() );
  5. txtAdvance.setText( String.valueOf( cost-deposit) );
  6. }
  7.  
  8. public void proceed()
  9. {
  10. btnOK.setLabel( "Clicked!" );
  11. }

If you do indeed need to do more analysis of the event which triggered the call you can access the event with the Page.getCurrentEvent() method and check the event type and properties. The EventHandler returned from the getCurrentEvent() method needs to be cast to the appropriate Handler type depending on how it was declared.

Data binding

Continuing with the example, we next add data bindings to put some content into the page. In the most basic setup, input controls can be bound to data stored in an XML file. Static data is data contained in flat files bundled with the application. Such data is often used for test purposes or during initial development of an application.

To work with data bindings a little configuration is needed and the startup file now requires the entry ModelData=simpledatasets.xml . The referenced file contains the static data. The data in the file is then bound to the input controls using TextBinding objects.

Inserting data bindings

  1. private void addBindings()
  2. {
  3. addBinding( new TextBinding(txtFirstname, "customer/firstname") );
  4. addBinding( new TextBinding(txtSurname, "customer/surname") );
  5. addBinding( new ListBinding(cmbAge, "customer/ageRanges") );
  6.  
  7. addBinding( new ListBinding(txtCarCost, "financial/carcost") );
  8. addBinding( new TextBinding(txtDeposit, "financial/deposit") );
  9. }

Validations

Validations are rules that can be used to check that user input is correct. Reusable validation rules are specified in an XML file (named validations.xml by default). For example:

Validation rules

  1. <Validations>
  2. <validation name="firstname" type="mandatory" msg="The [firstname] cannot be blank"
  3. mandatory="true"/>
  4. <validation name="surname" type="mandatory" msg="The [surname] cannot be blank"
  5. mandatory="true"/>
  6. <validation name="age" type="minmax" min="18" max="100"
  7. msg="The [age] ({value} [years]) must be between {min} [years] and {max} [years]"
  8. mandatory="true"/>
  9. </Validations>

Here three validation rules are specified for the example shown below. The first two rules merely check that some input has been provided while the third rule checks that an age value is within a minimum and maximum range.

Older versions of the Aria platform required an explicit call to setup the validation handling with a call to the ' setExceptionHandler ' method of the Page , this is no longer the case.

To use the validations we need to bind the validation to the components with a call to addValidation .

Binding the validatin rules to input fields

  1. addValidation( txtFirstname, "firstname" );
  2. addValidation( txtSurname, "surname" );
  3. addValidation( txtAge, "age" );

Putting the complete example together we get:

The complete example

  1. package org.formaria.samples.simple;
  2.  
  3. import org.formaria.aria.*;
  4. import org.formaria.aria.data.*;
  5.  
  6. public class DataBinding extends Page
  7. {
  8. Edit txtFirstname, txtSurname, txtCarCost, txtDeposit, txtAdvance;
  9. ComboBox cmbAge;
  10. Panel pnlClientInfo, pnlFinancialInfo, pnlButtons;
  11. Button btnOK, btnCancel;
  12.  
  13. public DataBinding()
  14. {
  15. pnlClientInfo = (Panel)componentFactory.addComponent( PANEL, 10, 10, 210, 80 );
  16. pnlFinancialInfo = (Panel)componentFactory.addComponent( PANEL, 10, 100, 210, 80 );
  17. pnlButtons = (Panel)componentFactory.addComponent( PANEL, 10, 200, 210, 40 );
  18. pnlClientInfo.setDrawFrame( Panel.BORDER_BEVEL );
  19. pnlFinancialInfo.setDrawFrame( Panel.BORDER_BEVEL );
  20. pnlButtons.setDrawFrame( Panel.BORDER_BEVEL );
  21. componentFactory.setParentComponent( pnlClientInfo );
  22. componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Firstname:", "prompt");
  23. txtFirstname = (Edit)componentFactory.addComponent( EDIT, 100, 10, 100, 20 );
  24. componentFactory.addComponent( LABEL, 5, 30, 90, 20, "Surname:", "prompt");
  25. txtSurname = (Edit)componentFactory.addComponent( EDIT, 100, 30, 100, 20 );
  26. componentFactory.addComponent( LABEL, 5, 50, 90, 20, "Age:", "prompt");
  27. cmbAge = (ComboBox)componentFactory.addComponent( COMBO, 100, 50, 100, 20,
  28. null, "Edit" );
  29.  
  30. componentFactory.setParentComponent( pnlFinancialInfo );
  31. componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Car cost:", "prompt");
  32. txtCarCost = (Edit)componentFactory.addComponent( EDIT, 100, 10, 100, 20 );
  33. componentFactory.addComponent( LABEL, 5, 30, 90, 20, "Deposit:", "prompt");
  34. txtDeposit = (Edit)componentFactory.addComponent( EDIT, 100, 30, 100, 20 );
  35. componentFactory.addComponent( LABEL, 5, 50, 90, 20, "Advance:", "prompt");
  36. txtAdvance = (Edit)componentFactory.addComponent( EDIT, 100, 50, 100, 20 );
  37. txtAdvance.setEnabled( false );
  38.  
  39. componentFactory.setParentComponent( pnlButtons );
  40. btnOK = (Button)componentFactory.addComponent( BUTTON, 40, 10, 60, 20, "Proceed" );
  41. btnCancel = (XButton)componentFactory.addComponent( BUTTON, 110, 10, 60, 20, "Cancel",
  42. "BtnReverse" );
  43.  
  44. addEvents();
  45. addBindings();
  46. }
  47.  
  48. public void pageActivated()
  49. {
  50. changeAdvance();
  51. }
  52.  
  53. private void addEvents()
  54. {
  55. addTextHandler( txtCarCost, "changeAdvance" );
  56. addTextHandler( txtDeposit, "changeAdvance" );
  57. addActionHandler( btnOK, "proceed" );
  58. }
  59.  
  60. private void addBindings()
  61. {
  62. addBinding( new TextBinding(txtFirstname, "customer/firstname") );
  63. addBinding( new TextBinding(txtSurname, "customer/surname") );
  64. addBinding( new ListBinding(cmbAge, "customer/ageRanges") );
  65. addBinding( new TextBinding(txtCarCost, "financial/carcost") );
  66. addBinding( new TextBinding(txtDeposit, "financial/deposit") );
  67. }
  68.  
  69. public void changeAdvance()
  70. {
  71. float cost = Float.valueOf( txtCarCost.getText() ).floatValue();
  72. float deposit = Float.valueOf( txtDeposit.getText() ).floatValue();
  73.  
  74. txtAdvance.setText( String.valueOf( cost - deposit) );
  75. }
  76.  
  77. public void proceed()
  78. {
  79. btnOK.setLabel( "Clicked!" );
  80. }
  81. }

The example makes use of the page's lifecycle method, pageActivated to call the changeAdvance method and update the page once all the components have been setup and the data loaded.

Annotations

If you use Java 5 and later you can take advantage of some annotations to save on coding. The annotations replicate features that can be coded directly in early versions of Java, so you are not missing out on any features if you cannot use Java 5 in this context. The annotations are as follows:

Annotations

Annotation

Role

@ Find

The find annotation performs the same function as the findComponent method, except that no typecasting is needed. An example of this is as follows:

 

@Find

XButton myButton;

 

This annotation finds the reference to an Button instance with the name ` myButton '.

@ Bind( path )

Like the find method above this annotation adds a binding to the specified path to the annotated component.

@ Validate( rule )

The Validate annotation associates the specified validation rule with the annotated component.

@ Event( method=, type= )

The Event annotation adds an event handler to the annotated component of the specified type. The response method is specified by the method argument.

The annotations are probably of most use to the Java programmer, but the @Find annotation can save a significant amount of boiler plate code for all users, thereby making the code more readable.

The annotations require that you include the appropriate classes from the org.formaria.annotation package. See your IDE's documentation for inserting and optimizing imports. (In NetBeans press ALT+SHIFT+F to find the necessary imports).

Buddy helpers

In some ways Java coding is more complex than the equivalent XML however once we get over that initial hurdle Java coding can be considerably simpler. One common example of this is the BuddyHelper class that is used to setup labeled components.

Using the BuddyHelper, one call can setup two (or even three) components. The helper is designed to add a label (and sometimes a suffix for say a dimension). The helper not only saves a call to the component factory for adding the label but it also makes it a little easier to get alignments correct. (Remember that layout managers are normally preferable to the null layout).

In the above example the name edit fields could be added with this code:

A buddy in use

  1. BuddyHelper buddy = new BuddyHelper( (StyleFactory)componentFactory );
  2. imgSingle = (Edit)buddy.addComponent( EDIT, 5, 100, 10, 200, 20,
  3. panelWidth, 28, "First Name", "", "prompt" );
  4. imgDouble = ( Edit )buddy.addComponent( EDIT, 5, 100, 30, 200, 20,
  5. panelWidth, 28, "Second Name", "", "prompt" );

Aria has many other helpers like this that can help in building Java application. These helpers are not restricted to UI component creating and they cover all areas of Aria including the data model, events and so on and are available throughout the lifecycle of the application.

Other helpers

Aria provides numerous helper classes aimed to simplify Java coding and to remove those verbose typecasting requirements.

Some helper classes

NavigationHelper

NavigationHelper extends Page by adding a few simple methods to assist in navigating from page to page.

DebugLogger

Keep track of errors and warnings and give some statistics when the application shuts down as a debugging aid.

MessageHelper

Formats messages (for 1.1.x JDKs)

NumberFormatter

A utility to help format numbers in accordance with the current Locale

DoubleAdapter

A helper for an double model field

IntegerAdapter

A helper for an integer model field

TableModelHelper

A utility class to help construct a HTML like table structure

ModelNodeHelper

A helper to eliminate some typecasting with the DataModel

Logging

For most applications it is useful to use logging during the develop stage of a project.

Aria provides the DebugLogger class to provide integrated logging features. This log class keeps track of the number of errors and warnings generated. The amount of logging output can also be configured by setting a log level in this class. Ultimately almost all logging can be suppressed by setting the log level to SILENT . This log level is application wide and can be set or reset at any point in the application lifecycle.

At startup the ' LogLevel ' property is used to set the initial value.

Aria itself uses logging extensively and this can lead to verbose console output. To further help cut down on the logging Aria can be built in two forms - with debug logging on or off. The BuildProperties.DEBUG flag controls this logging.

The flag is defined statically so that the compiler will eliminate any code that is excluded by the flag (when it is set to false). This same mechanism can be used to conditionally include logging code in your pages:

Adding debug logging

  1. if ( BuildProperties.DEBUG ) </P>
  2. DebugLogger.logWarning( "Some log message" );</P>

The actual mechanism used for logging can be controlled by the LogWriter startup parameter, but normally this is configured for you and should need no change.

Compilation and building

Java applications need to be compiled before they can be run. Aria provides facilities to do this building but it is also possible to build an application from the command line or from a third party tool.

Compiling an application is fairly straightforward as all the classes needed for a build are contained in the AriaRuntimeCommon.jar file. Individual files can be compiled with the Javac command on the command-line, but we recommend using the ANT build tool.

However, as the extent and complexity of a project increases it will be worth considering using the tools that Aria provides by way of NetBeans or tools such as Eclipse or their commercial equivalents such as JBuilder. These tools provide much more than just compilation support and can be of great benefit when testing and debugging a project.

Switching toolkits

When building an Aria application with Java it is necessary to derive the page from the Page class and add components to that page. As part of this process it is necessary to import the definitions of the various objects used in the page, including the Aria components. At this point it is therefore necessary to choose between the AWT and Swing toolkits as one or the other must be imported.

This import implies a dependency on one of the toolkits. However given judicious programming it should not be a big task to move from one toolkit to the other if you have only used Aria components and have avoided toolkit specific features.

In fact all you need do to convert from one toolkit to the other is to change the imports, recompile and test. Of course there will be some differences in the look and feel of the application but by in large the behavior should not change.

Switching toolkits is probably a rare occurrence yet it still remains a good idea to try and reduce dependencies with an application. If possible you should try and isolate dependence of one toolkit or another if only for the purpose of keeping your code clean and clear.

Beyond the basics

Sometimes what is built into a platform may not cover all your needs and it is necessary to add extensions and third party components. Aria is based on an open design and with Java at the core there are many ways to add and extend the platform.

For example if components outside of the basic framework are employed then they can be registered with Aria, See Choosing how to install components. and accessed with the techniques described above. Since third party components are not built-in Aria can only have a limited view or understanding of those components and therefore you may need to manipulate the components directly. Programming in Java allows you do this and once you obtain a reference to a component you can access all its features either directly through the public API or through reflection.

Section III: In Depth

Styles

Most applications try to use consistent styling and Aria provides support for consistent coloring and typography through its style manager. Aria's style management allows all the information and details of colors and fonts to be maintained separately from the rest of the UI description. Not only does this make it easy to reference particular style elements but it also makes it easy to switch styles say, for example, when rebranding an application for a different sets of end-users.

Using styles

In the introduction section we saw how styles could be applied when adding components either via XML or within Java code. Aria provides interactive visual tools to help design and use styles. Within Aria a style can be applied by selecting a component or several components and clicking on the chosen style in the style palette. The style name is then linked to the component so that any subsequent change to the style will change the look of linked components. If a component has an associated style then the styles palette's selection will be updated to show the selected style. The style palette presents a preview of the style and if you move the mouse over an individual style you should see a tooltip presented using the appropriate style, including the style's font.

Changing a style

A style can be modified by selecting the style in the style palette and then right clicking to popup the style's context menu. For most styles the simplest thing to do is to choose the Edit... option and pick colors and fonts that suit the particular use. The style edit dialog allows you to choose the font face and style plus the foreground and background colors. The styles are then saved to the styles file whenever the project is saved.

Color schemes

Aria extends the notion of styles by including a color scheme chooser to provide coherent sets of styles.

You can edit a set of styles as a color scheme by choosing the Color Scheme... option from the Style Palette's popup menu. The Color Chooser provides a convenient way of specifying sympathetic systems of colors and styles. The chooser consists of a number of areas for choosing the colors and then applying these colors to a set of styles. The color chooser has two parts, the first allows the choice of a set of sympathetic colors and the second allows selection of these colors for various usages. On the left hand side is a color wheel where you can choose a base color. This base color can be manipulated using two sliders for brightness and saturation. The hue can also be modified by clicking at some point within the inner circle. The Hue, Saturation and Brightness values can also be entered directly as numeric values. Clicking on the selected color preview (just below the color wheel) pops up a list of system colors (see the image on the right). The system colors can also be selected whenever the color wheel is available. Once the color has been chosen the colors and the variants are shown in a preview area. The number and type of variants displayed depend on the color scheme chosen. By default the scheme is monochromatic but other schemes can be chosen from the drop down list at the top of the chooser. Below the preview area is a table of colors and you can click on any on these to use it as the base color. On the second page of the chooser is an area where you can configure the styles. Simply click on the various sample texts to modify the foreground color, the background color or the font. A set of radio buttons allow you choose from these options. Once the set of styles has been selected click OK to save the styles under the specified style name. If a new style is being created the styles will be added to the style palette. Alternatively if the style name is already in use then the styles will be updated once the chooser is closed.

Loading styles

The style details are stored in an XML file pointed to by the startup parameter ' StyleFile '. In the absence of an entry or a file name the files ' styles.xml ' is used. A typical style file includes color and font information as below: A sample style file

  1. <styles>
  2. <style name="banner">
  3. <color_back value="ffffff"/>
  4. <color_fore value="0000ff"/>
  5. <font_face value="arial"/>
  6. <font_size value="10"/>
  7. <font_weight value="0"/>
  8. <font_italic value="0"/>
  9. </style>
  10. <style name="base">
  11. <color_back value="ffffff"/>
  12. <color_fore value="0000ff"/>
  13. <font_face value="arial"/>
  14. <font_size value="10"/>
  15. <font_weight value="0"/>
  16. <font_italic value="0"/>
  17. </style>
  18. <style name="Caption">
  19. <color_back value="ffffff"/>
  20. <color_fore value="008800"/>
  21. <font_face value="arial"/>
  22. <font_size value="11"/>
  23. <font_weight value="0"/>
  24. <font_italic value="0"/>
  25. <style name="CaptionSmall">
  26. <font_size value="8"/>
  27. <font_italic value="1"/>
  28. </style>
  29. </style>
  30. <style name="footer">
  31. <color_back value="ffffff"/>
  32. ...
  33. </styles>

Styles are determined from this hierarchy so that in the above example the style ' CaptionSmall ' is of the same font and color except with a smaller point size and italicized. (as of v2.0.6) Within the style file XML a color can also be specified as a decimal RGB value, an alpha channel value can also be specified as an option decimal value or as a hexval: Using decimal RGBA values

  1. <styles>
  2. <style name="banner">
  3. <color_back value="255,255,255"/>
  4. <color_fore value="0,0,255"/>
  5. <font_face value="arial"/>
  6. <font_size value="10"/>
  7. <font_weight value="0"/>
  8. <font_italic value="0"/>
  9. </style>
  10. <style name="base">
  11. <color_back value="ffffff80"/>
  12. <color_fore value="0,0,255,192"/>
  13. ...
  14. </styles>

Using System colors

System colors can also be used by naming the system color in place of an RGB value, thus Using SystemColors

  1. <styles>
  2. <style name="banner">
  3. <color_back value="activeCaption"/>
  4. <color_fore value="activeCaptionText"/>
  5. <font_face value="arial"/>
  6. <font_size value="10"/>
  7. <font_weight value="0"/>
  8. <font_italic value="0"/>
  9. </style>

The system colors are dependant on the user preferences and may vary from user to user and machine to machine. The system color properties are:

System Colors

Name

Usage

desktop

The color rendered for the background of the desktop.

activeCaption

The color rendered for the window-title background of the currently active window.

activeCaptionText

he color rendered for the window-title text of the currently active window.

activeCaptionBorder

The color rendered for the border around the currently active window.

inactiveCaption

The color rendered for the window-title background of inactive windows.

inactiveCaptionText

The color rendered for the window-title text of inactive windows.

inactiveCaptionBorder

The color rendered for the border around inactive windows.

window

The color rendered for the background of interior regions inside windows.

windowBorder

The color rendered for the border around interior regions inside windows.

windowText

The color rendered for text of interior regions inside windows.

menu

The color rendered for the background of menus.

menuText

The color rendered for the text of menus.

text

The color rendered for the background of text control objects, such as textfields and comboboxes.

textText

The color rendered for the text of text control objects, such as textfields and comboboxes

textHighlight

The color rendered for the background of selected items, such as in menus, comboboxes, and text.

textHighlightText

The color rendered for the text of selected items, such as in menus, comboboxes, and text.

textInactiveText

The color rendered for the text of inactive items, such as in menus.

control

The color rendered for the background of control panels and control objects, such as pushbuttons.

controlText

The color rendered for the text of control panels and control objects, such as pushbuttons.

controlHighlight

The color rendered for light areas of 3D control objects, such as pushbuttons. This color is typically derived from the control background color to provide a 3D effect.

controlLtHighlight

The color rendered for highlight areas of 3D control objects, such as pushbuttons. This color is typically derived from the control background color to provide a 3D effect.

controlShadow

The color rendered for shadow areas of 3D control objects, such as pushbuttons. This color is typically derived from the control background color to provide a 3D effect.

controlDkShadow

The color rendered for dark shadow areas on 3D control objects, such as pushbuttons. This color is typically derived from the control background color to provide a 3D effect.

scrollbar

The color rendered for the background of scrollbars.

info

The color rendered for the background of tooltips or spot help.

infoText

The color rendered for the text of tooltips or spot help.

Extended Styles

Styles can be extended to add attributes other than the basic color and font attributes. However the use of these extra attributes is dependant upon the context such as the component or class using the style. The extra attributes are automatically added for the style when the style is loaded. Once the stle has been loaded it is marked as closed and any attempt to access an unrecognised style name will cause an error (this can be changed with the XStyleEx.setClosed(...) method). An example usage of the extended styles is the validation style, which uses three colors to specify the feedback colors for the validation states. The validations styles could thus be specified as: Specifying extended styles

  1. <style name="banner" extended="true">
  2. <colorNormal value="ffffff"/>
  3. <colorWarn value="ffffd5"/>
  4. <colorFail value="ffe6d5"/>
  5. </style>

Note the extended attribute for the style. By adding extra style attributes you may be able to provide additional consistency within the application as attributes for various classes can be specified on an application wide basis instead of via a class by class or component by component basis. In many cases you can specify the attribute values for components, for example alignments and borders. In applying a style the framework will attempt to set component attributes using the style name and value via either the XAttributedComponent interface or via Reflection. The basic types such as String, int, double, float and boolean values can be accomodated. The built-in styles are handled directly as described above and set the component's colors and font. Setting right aligned label styles and properties

  1. <styles></P>
  2. <style name="labelBold">
  3. <color_back value="255,255,255"/>
  4. <color_fore value="0,0,0"/>
  5. <font_face value="arial"/>
  6. <font_size value="10"/>
  7. <font_weight value="1"/>
  8. <font_italic value="0"/>
  9. <alignment value="Right"/>
  10. </style></P>
  11. ...

Applying styles

To apply a style to a component you just need to reference the style in the page's XML declaration. Thus applying style to an address form becomes: Style usage

  1. <Page>
  2. <Components>
  3. <Label x="42" y="61" w="103" h="20" content="First Name:" alignment="Right" style="Caption"/>
  4. <Label x="42" y="85" w="102" h="20" content="Last Name:" alignment="Right" style="Caption"/>
  5. <Label x="42" y="116" w="102" h="20" content="Telephone:" alignment="Right" style="Caption"/>
  6. <Label x="42" y="137" w="102" h="20" content="Fax:" alignment="Right" style="Caption"/>
  7. <Label x="42" y="161" w="102" h="20" content="Mobile:" alignment="Right" style="Caption"/>
  8. <Label x="42" y="196" w="102" h="20" content="Address:" alignment="Right" style="Caption"/>
  9. <Edit name="firstNameEdit" x="154" y="62" w="100" h="20"/>
  10. <Edit name="lastNameEdit" x="154" y="85" w="100" h="20"/>
  11. <Edit name="phoneEdit" x="155" y="115" w="100" h="20"/>
  12. <Edit name="faxEdit" x="155" y="137" w="100" h="20" />
  13. <Edit name="mobileEdit" x="154" y="160" w="100" h="20" />
  14. <Edit name="address1Edit" x="155" y="198" w="248" h="20" />
  15. <Edit name="address2Edit" x="155" y="222" w="248" h="20" />
  16. <Edit name="address3Edit" x="155" y="246" w="249" h="20" />
  17. </Components>
  18. </Page>

Related issues

Colors and fonts are not the only style related things that should be consistent within an application. Layouts, naming, data representations, translations and so on should also be consistent. Consistency will make it easier for a user to grasp the concepts behind the application and it will make it easier to understand the mechanism used within the application for various tasks. Logically grouping elements can also assist readability. So, for example placing related components together within a framed panel gives a visual clue as to the association of those components. Subtle shading of panels can also assist in giving the necessary visual clues about the role of an individual panel particularly if the coloring is reinforced by the behavior you code into the applications.

Layout

In terms or readability it is often said that the average person can optimally perceive about seven pieces of information. In an application it is often difficult to limit the number of items or components displayed at once. Therefore style can play an important role in reducing apparent clutter and confusion. The style manager can go along way to helping produce a harmonious color scheme and if judiciously employed such a scheme may allow you make better use of the on-screen space. To maximize such benefit it is important that your layout is consistent. Careful consideration should be given to alignments, spacing and grouping of components. All of these features can greatly assist in the smooth flow of action within an application. A later chapter (See Layout.) details how layouts can be controlled within an application. With dynamic content it is always important to consider how the content will appear. Aria makes it easy to dynamically modify the content of a component but this generally doesn't mean that the layout of that component will be modified. Particularly for elements that display text it is important to note the interaction of fonts and layouts. Some fonts are narrower than others or have different distributions of character widths and therefore it is important to test your application with a representative set of data.

Flow

Just as layout is important, the flow of text and components can have a major impact on the user experience. Generally it is best to keep it simple: left, right, top to bottom. Keep it simple, reinforce the visual signals, update consistently.

Naming

Styles should be named consistently for two reasons. Firstly from a programming point of view a consistent naming convention will make reading of code easier and hence development and maintenance will be easier. We recommend that you use a convention similar to the naming convention you would use in programming Java. Secondly a consistent style naming convention is important where styles are being modified and particularly where the Color Chooser is being used. The chooser can modify existing styles depending on the style name and therefore a consistent naming convention will help avoid confusion when styles are modified in this way.

Data/formatting

Some values such as numeric, date or monetary values may be formatted specially and styles can be used to reinforce or highlight important values such as negative monetary values.

Setting the system look and feel

The system look and feel can be set up using a look and feel (LAF) installer. The LAF installer is controlled by a startup parameter " LAF ". The value of this parameter refers to a helper class that installs the LAF and takes care of the initialization. This setting is only appropriate for Swing applications as changing the LAF is not supported in the AWT.

Setting the Look and Feel

In Swing you can alter the look and feel of an application by setting the 'Look and Feel' manager for the application. The look and feel (LAF) affects the visual appearance of components and some of the soft behavioral attributes, like highlighting, rounding of button edges and so on. The LAF may also have a broader impact by changing indentations, component sizes and layout. Setting the LAF is fairly straightforward within Aria. The project settings pages include the option to choose the LAF. (it is on the files page as somewhat tenuously you need to choose a configuration file for the synth LAF) Adding a new LAF is also possible and Aria makes this even easier than normal by allowing you to simply plug-in the look and feel via a startup parameter. Aria comes with support for a few popular LAFs, but it is also easy to add support for your favorite LAF. The component class for setting the LAF is part of the Open Source Aria and hence you can review the source code for the LAF installer to see how to build an installer for some other LAF. The LAF is set with the following startup parameter

Setting the look and feel

LAF=org.formaria.laf.SynthInstaller

This setting refers to a helper class that installs the LAF and takes care of any initialization that is necessary. Aria lists a few popular LAFs, including the default LAF, JGoodies Looks LAF, Windows and the Synth LAF. Of these, the Synth LAF is probably the most interesting as it is configured via XML.

Configuring Synth

The Synth LAF requires a number of extra parameters to be configured to operate correctly.

Synth parameters in the start-up file

SynthConfigFile=demo.xml SynthResourceLoader=mypackage.MyClass

These parameters may vary significantly depending on the content of the Synth configuration file. The SynthResourceLoader is the name of a class that will be used to load the resources used by the Synth LAF. The class does not need to do anything special as it merely provides a (classloader) route to the resources. When the Synth LAF is chosen a number of new styles are added to the project if they do not already exist. These styles are used by the LAF and you can customize them to alter the look. Note that the Synth LAF provides rendering for many aspects of the components and the LAF may therefore override the styles you have chosen for individual components. Some of the built-in components have an option to suppress the LAF and use the styles you explicitly apply to the component.

SkinBuilder

A sample project ` SkinBuilder ' is included in Aria 3.0, this application is another demo of Aria, but it can be used to configure the Synth Look and Feel, including choosing artwork, setting properties and colorizing the LAF . See the accompanying documentation for more details.

Using styles with Synth

While the Synth look and feel can be used out of the box it is often desirable to connect the look and feel to the application's styles for added consistency. The Synth look and feel is configured via an XML file and this configuration file can itself be preprocessed to include colors and fonts from the applications look and feel. Some special expressions can be embedded in the XML and when processed these are converted into the color and font declarations expected by Synth. Scalable Vector Graphics ( SVG) images can also be used with Synth by way of the preprocessor. Aria can convert a referenced SVG into a PNG raster graphic for use in the final application. Using SVG in this way allows you to create sophisticated icons and buttons for your application and have these images scale to the required size.

Synth substitution expression

Expression

Example Result

Usage

${getFontFace(styleName)}

Arial

Gets the font face name from the style

${getFontSize(styleName)}

10

Get the font point size from the style

${getColorBackground(styleName)}

F0C2E9

Get the foreground color from the style

${getColorForeground(styleName)}

F00000

Get the background color from the style

${getFontWeight(styleName)}

BOLD

Get the font weight from the style

${getFontItalic(styleName)}

ITALIC

Get the font italic flag from the style

${svgToPng(styleName)}

images/button.png

Convert an SVG image to a PNG. Optional width and height attributes can also be specified after the source image name. If they are omitted a 50x50 image is created. A PNG image sometimes includes an optional preview of the image.

${svgToTiff(styleName)}

images/button.tif

Convert an SVG image to a TIFF. Optional width and height attributes can also be specified after the source image name. If they are omitted a 50x50 image is created.

Using SVG and Synth

Just as the Synth file can be processed so to can the preprocessed SVG files. SVG uses a slightly different definition of colors so different expressions are required but the concepts are the same. The substitution expressions also come in a number of variations to help support generation of gradients from a single style.

SVG substitution expression

Expression

Example Result

Usage

${getColorForegroundRgb(styleName)}

rgb(108,128,255)

Gets the RGB foreground color from the style

${getColorForegroundHex(styleName)}

F0C2E9

Gets the HEX foreground color from the style

${getColorBackgroundRgb(styleName)}

rgb(108,128,255)

Gets the RGB background color from the style

${getColorBackgroundHex(styleName)}

F00000

Gets the HEX backgroundcolor from the style

${getHsbColorForegroundRgb(styleName)}

rgb(108,128,255)

Gets the RGB foreground color from the style

${getHsbColorForegroundHex(styleName)}

F0C2E9

Gets the HEX foreground color from the style

${getHsbColorBackgroundRgb(styleName)}

rgb(108,128,255)

Gets the RGB background color from the style

${getHsbColorBackgroundHex(styleName)}

F0C2E9

Gets the HEX background color from the style

Each expression in the table above can have just a style name as an argument or it can have the style name plus percentage values for each color channel. Thus an expression such as getColorForegroundRgb(HeadlineStyle,50,60,80) obtains the color from the style, splits out the RGB color channels and returns a color that scales the channel values by the percentages given. So if the red value was 240 the example expression would return a color with a red value of 50% or 120. Similarly if the percentage given is between 100% and 200% the color value is scaled up to 255. In this way a red value of 120 and a percentage of 150 would result in a color with a red value of 187 (i.e. 120 + (255-120)*50/100). The expression set also includes a variation where instead of scaling the RGB values the HSB values are scaled. In some cases it may be easier to specify the gradient color stops as variations in saturation or brightness values. The scaling of the HSB values works in the same way as the RGB values. Aria automatically adds a set of styles for Synth when you select the Synth LAF. These styles are the styles that are used in the preconfigured Synth LAF that is part of Aria. However you can further customize the look and feel to use whatever graphics files and styles that you like, the configuration file is just another XML file that you can edit. To add another style to the Synth LAF just add the matching style within Aria or within the style file. Then embed the relevant expression from the list above in your Synth configuration file. The Synth LAF requires the Batik SVG engine to process SVG images. The libraries for Batik must be part of your project for this process to work correctly. More information on Batik can be found on the Batik website.

Panel backgrounds

Synth can be used to paint many components including the panel backgrounds but in some cases a different look or style is desirable for painting panels to help distinguish part of an application. A custom painter class can be used for such painting, the code for configuring such a background painter is shown below:

Setting a background painter

The example below shows a number of panels using this custom painter to display a gradient background with a watermark logo.

Painters

Aria 2.0 introduced the notion of painters much like the painters used within the Synth look and feel, and since then the use of painters has become more widespread. Aria 3.0 adopted the SwingLabs painter API in an effort to conform to standards and to make a wider range of painters available. The painters API is straightforward.

The painters API

public void paint( Graphics2D g2d, JComponent comp, int w, int h );

and can be used in a varient of Aria and Aria components. The painter is normally responsible for painting the entire client area of the component, but of course it is open to configuration. The painter class need not extend a component and therefore it acts purely as helper class. Some painters included in Aria are:

 

Painter

Appearance/Role

BandPainter

Fills the client area with a number of coloured bands, graduating from the foreground colour to the background colour.

FlarePainter

Fills the client area with a radial gradient using the foreground and background colours

TitlePainter

Extends the GradientBackground by adding a caption

GradientBackground

Fills the background with a gradient. The gradient uses the background and foreground colours. The painter can be configured to `nest' its gradient into the parent coordinate space so that multiple panels appear to share the same gradient.

SvgPainter

A painter based on an SVG images. See the section SVG Support for more details. The painter can interact with the mouse for various effects.

ImagePainter

Paints an image in the client area.

Style guidelines

A full discussion of styles is beyond the scope of this guide, however a number of style guidelines have been published and depending on your target platform and personal tastes it may be worth consulting these guidelines for clues as to best practice.

Look and feel resources

Java Look and Feel Design Guidelines: http://java.sun.com/products/jlf/

Introduction to the Apple Human Interface Guidelines: http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/

Official Guidelines for User Interface Developers and Designers: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/welcome.asp

Palm OS User Interface Guidelines: www.palmos.com/dev/support/docs/uiguidelines.pdf

SwingLabs Painter http://javadesktop.org/swinglabs/build/weekly/latest/swingx-HEAD/javadoc/org/jdesktop/swingx/painter/package-summary.html

 

Layout Managers

Layout

By default Aria uses absolute positioning of components, with each component having explicitly specified size and location. Frequently it is more convenient to use some of the additional layout facilities found in Aria and in Java to both improve the overall appearance and simplify development.

Aria even provides its own layout manager to support interactive visual layout above and beyond what is possible with explicit layout while providing the runtime resize and position behavior that are frequently hard to achieve with other layout managers.

Default layout

As mentioned in the introduction Aria assumes that each component's coordinates are specified explicitly. Simply speaking this means that each component must have a specified X, Y location and a width and size dimension. The dimensions are all in pixels and do not vary with the size of the application window or the page container.

Limitations of explicit layout

Perhaps the greatest limitation of explicit layout is that the pages do not resize with changes to the application window and therefore if the application is made full screen it does not make good use of the available screen space.

Another significant limitation of the explicit layout is that it is not always possible to know the size needed for a component or a component's content in advance. Dynamic sizing of components can alleviate such problems.

Later in this chapter we will see how Java's Layout Manager facility has been adapted to help address some of the issues.

Aria provides a number of tools to support interactive positioning of controls. The tools include grids and guides. These tools can be used with several of the supported layout managers but work best with Aria's own layout managers; the GuideLayout and the ScaleLayout.

Alignment tools

Aria provides several tools for aligning components within a container and even spanning containers. These tools are located on the toolbar within the page designer and perform the following functions:

Alignment and Justification Tools

Title

Usage

 

Align Bottom - aligns the bottom edges of all the selected components. The components are moved and retain their current height.

 

Align Centre - aligns the centres of all the selected components. The components are moved and retain their current width.

 

Align Left - aligns the left edges of all the selected components. The components are moved and retain their current width.

 

Align Right - aligns the right edges of all the selected components. The components are moved and retain their current width.

 

Align Top - aligns the top edges of all the selected components. The components are moved and retain their current height.

 

Justify Horizontal - aligns the left and right edges of all the selected components. The components sized according to the minimum left and maximum right hand coordinates.

 

Justify Vertical - aligns the top and bottom edges of all the selected components. The components sized according to the minimum top and maximum bottom coordinates

 

Space Vertical - Adjusts the space between the selected components so that the space is equal distributed along the vertical axis

 

Space Horizontal - Adjusts the space between the selected components so that the space is equal distributed alongthe horizontal axis

The alignment and justification tools are primarily designed to work with explicit layouts and will adjust the coordinates of the selected components. Use of the alignment tools with pages controlled by layout managers can lead to results that vary according to the specific layout manager and its current settings.

Grids and Guides

In the page designer the page being edited can be shown within a grid and with layout guides. The designer shows rulers to the top and left of the page, set out in pixel units.

The designer allows components to optionally snap to the grid. The selection of this option and the granularity of the grid can be configured within the page designer. In the top left hand corner or the designer is a button, that when pressed toggles the display mode. When in the configuration mode the rulers are shown with a red tint.

Once in the configuration mode you may right click anywhere on the page to get a context menu giving some options to control the layout. Once configuration has been completed just click the button in the top left to return to the page editing mode.

The options that can be configured in this way include:

 

Option

Role

Show Guides

Shows the alignment guides

Show Grid

Show the grid (with a minimum display spacing of 10 pixels).

Snap to grid

Turns on or off the snap to grid facility where by components that are moved align themselves to a grid point.

Snap to guides

Turns on or off the facility whereby components snap to the nearest guide.

Set Grid Spacing

The separation of grid points in pixels.

Guide sensitivity

Specifies the distance in pixels within which a component must be for it to be affected by a guide line.

Setup Columns

Displays a dialog that allows the addition or guides to support rows and columns for layout purposes

Guides

Drawing on the techniques used in desktop publishing and document layout, Aria includes a facility to use guides. Guides are vertical or horizontal lines on the page to which components snap or gravitate while being positioned during page layout.

Guides are configured on a page by page basis rather than at the level of individual panels. Guides therefore support layout across panels so it is easy to align components on a page wide basis rather than solely within an individual container as is the case with most layout managers. Guides are also visually intuitive so little effort is required to learn how guides function and indeed there should be few surprises when using guide layouts unlike layouts such as the GridBagLayout which is notoriously difficult to use.

Adding guides

Guides can be configured on a page by page basis or on an application wide basis (as master guides) to help enforce layout rules across pages.

Within the page designer adding guides is carried out by

  1. Clicking the button in the top left intersection of the rulers.
  2. Click and drag from either of the rulers onto the page
  3. Release the mouse at the location for the new guide
  4. Drag an existing guide to adjust its position
  5. Click the button in the top left to exit the guide editing mode.

To remove a guide simply enter the guide editing mode and drag a guide back to the ruler.

Individual guides can then be configured by right clicking on them and choosing the Guide Options... menu option. This option allows control over how the guide is positioned at runtime.

Guides can be positioned relative to one another or relative to the size at which the page is displayed so as to give a controlled final layout. The coordinates system used for the guide can also be expressed in relative terms of the page size instead of the absolute pixel size used at design time. The guide ID shown in the dialog is used to reference the guide by the constraint specified for each component on the panel controlled by the guide layout manager.

Guide options

Parameter

Usage

ID

An ID used by the component constraint to identify the guide. This option is read-only and is automatically configured by the editor. Guides are renumbered as they are repositions so that the leftmost or topmost guide is always zero.

Orientation

A read-only property identifying the guide as vertical or horizontal

Position

The guide position. Absolute positioning means the guide is position relative to the top left of the page. Relative positioning means the guide is position relative to the previous guide (to the left or above).

Coordinate

The coordinates express the position of the guide either in absolute terms of pixels or in terms of the page size (scaled). For a scaled coordinate system the location is expressed as a percentage of the page size.

Location

The guide location is its position. For a relative position the location value is the offset from the previous guide, whereas for an absolute location the value is the distance from the top left of the page.

Minimum

Not yet implemented

Maximum

Not yet implemented

Several more options are available for configuration of the guides:

The area of guide sensitivity, that is the area within which a component will snap to the guide when moved can be highlighted (as illustrated above) by choosing the Show snap zone context menu option. The context menu is displayed by right clicking on a guide in edit mode. To enter the guide's edit mode, toggle the button to the top left of the page at the vertex of the two rulers. In this mode the rulers are shown with a red background. Some of the guide options are context sensitive and depend on the guide on which the mouse is clicked. The options are described below:

Guide configuration options

Menu option

Function

Snap to guides

Toggles snapping of components to the guides

Snap to grid

Toggles snapping of components to the grid.

Snap to hints

Snap the component to the hints. Matches one component to the edges of another.

Show guides

Toggle display of the guides

Show snap zone

Toggle display of the zone within which components snap to the guides.

Show grid

Toggles display of the grid. The grid is shown as a set of dotted lines. Note that at lower grid spacings not every grid line is shown as doing so would obscure the display.

Set grid spacing

Set the grid spacing in pixels

Setup columns...

Setup a set of column and/or row guides

Guide options...

Configure an individual guides. This option depends on the guide closest to the point where the mouse was clicked.

Snapping to guides

As a component or set of components is dragged about within the page designer the edges of the components will automatically snap to the guide whenever within range.

Guides will tend to cause a component to resize as a component is dragged but once a component goes out of range it will snap back to its original size.

Snapping to hints

In addition to the snapping of components to the guides the editor will show layout hints. These hints correspond to the edges of other components on the form. The hints therefore act as a form of automatic alignment of components. Later we plan to enhance this mechanism to support the alignment of baselines and the preferred platform component spacings (both features of the forthcoming JDK 6).

Setting up Columns

While in the configuration mode you can choose to setup columns and rows to assist layout. The GuideChooser dialog appears when this option is chosen.

You can use this dialog to quickly setup regularly spaced columns and rows. Once the guides have been added you may reposition the guides to best suit your layout needs.

A form will typically require a number of different guide types with some absolute position and some relative or scaled positioning.

Using guides

While it is possible to use guides purely for design time layout assistance the guides are of added benefit at runtime when a page is resized. To actually use the guides for this purpose it is necessary to use the GuideLayout manager. To use this Layout manager select the panel or container for which you wish to apply this option and set the layout property in the component's property sheet (normally to the bottom right of the page designer).

You may not see any immediate effect of choosing this layout manager but if you view the page's XML you will see that additional constraint attributes are added to the components belonging to the affected panel. The constraints refer to the guide ID which you may have noticed while setting up the guides. The guides are numbered from zero, up from left to right and from top to bottom. The constraint is thus just the list of guides to which the left, top, right and bottom edges of the component are bound (a -1 value indicates an unbound edge).

An example guide layout

To illustrate the use of the guide layout let's look at a sample form. The form is made up of two columns of labels and edits. Therefore a four column guide layout is created so that we can specify the spacing between the labels and edits and also the spacing between the columns, just as is shown above.

The leftmost guide in each column and the last guide (vertical guides 0, 3 and 7) are scaled guides so that each holds its position relative to the page. The next guide is a relative guide specifying a fixed width for the label. Following this another relative guide with a fixed width of 10 pixels is used to specify the spacing between label and edit. The edit field is then left to use the remaining space between the fixed guides and the scaled guides.

The XML code for the complete form layout is shown below.

Guide layout

  1. <Page Layout="Guide">
  2. <Layout/>
  3. <Guides width="640" height="480">
  4. <vert >
  5. <Guide id="0" pos="10.0" type="abs" coords="abs" />
  6. <Guide id="1" pos="150.0" type="rel" coords="abs" prev="0"}
  7. <Guide id="2" pos="10.0" type="rel" coords="abs" prev="1"}
  8. <Guide id="3" pos="49.063" type="abs" coords="rel"}
  9. <Guide id="4" pos="10.0" type="rel" coords="abs" prev="3"
  10. <Guide id="5" pos="150.0" type="rel" coords="abs" prev="4"
  11. <Guide id="6" pos="10.0" type="rel" coords="abs" prev="5"
  12. <Guide id="7" pos="98.125" type="abs" coords="rel"
  13. </vert >
  14.  
  15. <horz >
  16. <Guide id="0" pos="10.0" type="abs" coords="abs"}
  17. <Guide id="1" pos="20.0" type="rel" coords="abs" prev="0"}
  18. <Guide id="2" pos="10.0" type="rel" coords="abs" prev="1"}
  19. <Guide id="3" pos="20.0" type="rel" coords="abs" prev="2"}
  20. <Guide id="4" pos="10.0" type="rel" coords="abs" prev="3"}
  21. <Guide id="5" pos="20.0" type="rel" coords="abs" prev="4"}
  22. <Guide id="6" pos="10.0" type="rel" coords="abs" prev="5"}
  23. <Guide id="7" pos="20.0" type="rel" coords="abs" prev="6"}
  24. <Guide id="8" pos="30.0" type="rel" coords="abs" prev="7"}
  25. <Guide id="9" pos="97.917" type="abs" coords="rel"/>
  26. <horz >
  27. </Guides>
  28.  
  29. <Components>
  30. <!-- guide constraints: left, top, right, bottom -->
  31. <Label x="10" y="10" w="150" h="20" constraint="0,0,1,1" content="First Name"/>
  32. <Edit x="170" y="10" w="144" h="20" constraint="2,0,3,1"/>
  33. <Edit x="170" y="40" w="144" h="20" constraint="2,2,3,3"/>
  34. <Edit x="170" y="70" w="144" h="20" constraint="2,4,3,5"/>
  35. <Edit x="170" y="100" w="144" h="20" constraint="2,6,3,7"/>
  36. <Label x="10" y="40" w="150" h="20" constraint="0,2,1,3" content="Second Name"/>
  37.  
  38. <Label x="10" y="70" w="150" h="20" constraint="0,4,1,5" content="Phone"/>
  39. <Label x="10" y="100" w="150" h="20" constraint="0,6,1,7" content="Fax"/>
  40. <Edit x="485" y="11" w="144" h="19" constraint="6,0,7,1"/>
  41. <Edit x="484" y="40" w="144" h="21" constraint="6,2,7,3"/>
  42. <Edit x="484" y="69" w="146" h="22" constraint="6,4,7,5"/>
  43. <Label x="325" y="10" w="150" h="20" constraint="4,0,5,1" content="Address"/>
  44. <Label x="324" y="41" w="152" h="20" constraint="4,2,5,3" content="Street"/>
  45. <Label x="324" y="69" w="150" h="22" constraint="4,4,5,5" content="Town"/>
  46. </Components>
  47. </Page>

The constraints for each component specify the guide ID for the left, top, right and bottom edges. If the component is not bound to a guide then the ID can be specified as -1 and the x, y, w or h dimensions will be used to size and position the component as needed.

As of version 2.0.6 the guides can be stored in a separate file for reuse. Using this mechanism the guide files can be included using the following syntax:

Including guides

  1. <Guides include="guides/myGuideFile.xml">

By convention the guide files are included in the guides subfolder of the pages folder. The .xml extension is optional.

To see the guide layout in action we then resize the form. In the resized form each column still occupies half of the page although the relative size of the edit fields has increased.

Reusing guides

Sometimes, to promote consistency across forms you may wish to reuse a set of guides. So far we have no explicit support for such a process with Aria, however you can achieve the same effect by manually cutting and pasting the guide setup from one form to another.

The entire guide declaration is contained in the Guides element at the start of each page, with a Guide entry for each vertical and horizontal guide.

Layout managers

Java introduced the concept of layout managers as a way of managing cross platform layout issues. There are a wide variety of layout managers available within the core Java libraries and from third parties. However for reasons of simplicity only a core set of layout managers is supported within Aria.

Layout types

LayoutManager

Description

NULL

This really isn't a layout manager and instead components are positioned according to their absolute coordinates, specified at design time. This layout will not cause components to be repositioned as the container is resized.

Border

A built-in Java AWT and Swing layout manager dividing the container into five predefined areas. See the Java documentation for more details.

Flow

A built-in Java AWT and Swing layout manager. Components are arranged in the order in which they are added either as a row or column and depending on their preferred size. See the Java documentation for more details.

Card

A built-in Java AWT and Swing layout manager. Arranges the component in a stack and displays only the selected component so that it completely fills the container. The first component added to a CardLayout object is the visible component when the container is first displayed. See the Java documentation for more details.

Box

A built-in Java Swing layout manager. Similar to a FlowLayout, it arranges components along the X or Y axis. Provides slightly more control over the layout than the FlowLayout. See the Java documentation for more details.

Grid

A built-in Java AWT and Swing layout manager. Arranges components in a grid or table like structure. See the Java documentation for more details.

GridBag

A built-in Java AWT and Swing layout manager. Horribly complex to arrange and use. The GridBagLayout class is a flexible layout manager that aligns components vertically and horizontally, without requiring that the components be of the same size. Each GridBagLayout object maintains a dynamic, rectangular grid of cells, with each component occupying one or more cells, called its display area. See the Java documentation for more details.

Spring

A built-in Java AWT and Swing layout manager. See the Java documentation for more details.

Scale

An Aria layout manager that uses the absolute positions of the components and scales them in proportion to the container's runtime size. Optionally allows fonts to be scaled.

Guide

An extension of the ScaleLayout but using the Guides to add extra control over the scaling. The constraints for this layout are set automatically

Aria also includes a number of other layout managers used by some of its builders and wizards but not yet directly supported by the editor or by the basic Aria setup. These layout managers include:

Extra layouts

LayoutManager

Description

ColumnLayout

A columnar layout for use in forms or the like, where a set of components is associated with a set of labels or other such controls and laid out in columns.

The components are added in rows, but align to the columns. An example usage is in creating forms where a left hand column may contain labels or captions while the right hand column may contain the input fields. Indentation, spacing and padding may be added to control the layout and define the location of individual compoents.

LayerLayout

The LayerLayout is intented for use with pages and panels where you want to overlay one set of components with another. All the children of the container with the LayerLayout are given the same size and hence overlay one another. The components are located in the order in which they are created. An application may have to set the opaque property of the panels added to the layers so that the layers appear as expected.

The layering is intended to allow things like background decorations to be added and controlled easily. Layering may also be used to implement features such as overlays and modal/lock-out behavior, say for example overlaying a progress animation during a long running operation.

HtmlFormLayout

A layout mimicing the way in which HTML lays out its components

While these layout managers are not yet supported by the editor you can still make use of them by coding their use directly in Java. Some of the examples shipped with Aria make use of these layouts.

Component hierarchies

It is often a good idea to subdivide an application and its pages into different areas.

At the highest level and application may use the notion of a frameset to subdivide the screen or page. Typically a frameset is used where there are persistent elements to an application such as headers, footers or sidebars. Framesets are often used for navigation controls like toolbars, progress meters, structural views (tree views) and so on.

Within an individual page further nesting of content may be used with various types of panels, scrollpanes, splitters and tab controls.

Generally the more an application can subdivide the hierarchy of components the easier it is to control the layout of those controls.

The coordinates of child panels and of the components nested in a child panel are always relative to the parent or owner of the component or panel. The coordinates are also always measured from the top left of the panel that owns the component.

Using a layout manager

In using a layout manager in Aria there are two steps to consider. First the parent container for a component must specify the type of layout manager it employs and secondly each component should specify the constraints that the layout manager should use to lay it out.

Using XML the layout is added to the page or parent component as in the example below:

Sample Page using Layout Managers

  1. <Page class="org.formaria.testpilot.ApplicationRecorder" layout="border">
  2. <Components>
  3. <ScrollPane constraint="center">
  4. <Table2 name="table" horizontal_scrollbar="as needed"
  5. vertical_scrollbar="always"/>
  6. </ScrollPane>
  7. <Panel name="SouthPanel" constraint="south" layout="flow" style="base">
  8. <Button name="btnOption" content="Options"/>
  9. <Button name="btnStart" content="Start" enabled="false"/>
  10. <Button name="btnPause" content="Pause"/>
  11. <Button name="btnSave" content="Save" enabled="false"/>
  12. <Button name="btnBack" content="Back"/>
  13. <Button name="btnAssert" content="Assert" enabled="false"/>
  14. </Panel>
  15. </Components>
  16. <Events>
  17. <Event method="setOptions" target="btnOption" type="MouseHandler"/>
  18. <Event method="startScript" target="btnStart" type="MouseHandler"/>
  19. <Event method="addPause" target="btnPause" type="MouseHandler"/>
  20. <Event method="saveScript" target="btnSave" type="MouseHandler"/>
  21. <Event method="goBack" target="btnBack" type="MouseHandler"/>
  22. <Event method="addAssert" target="btnAssert" type="MouseHandler"/>
  23. </Events>
  24. </Page>

In the example the page is given a border layout and its children, the scroll pane and the panel are given constraints that apply to the border layout. The panel in turn is given a flow layout, but in the case of a flow layout the only constraint is the order in which items are added to the container.

The layout in individual components is controlled by the constraint attribute. The value of the constraint attribute varies according to the layout manager in use. The AriaEditor editor will present a list of the constraints that are appropriate for the particular layout manager.

Framesets

In earlier chapters (See Frames setup.) we saw how framesets could be added and configured. The frameset is an important layout device for applications in that different frame elements can have different lifecycles. Frequently the top, left and bottom elements in a frameset are unchanging within an application, and this long running context provides a space for common features such as toolbars, navigation bars, banners and feedback information.

The frameset is control by a border layout (see See Layout managers.) and you can update each named area of the frameset independently of one another. The frames or named areas within the frameset are termed targets within Aria. Using the targets is straightforward.

We have seen how a page can be displayed by calling the showPage(...) method:

Showing a single page

  1. // Via the page manager member variable of the page
  2. pageMgr.showPage( "DetailsPage" );

A second version of the showPage method takes an additional argument that specifies the target area:

Showing a single page in the content target area

  1. // The manager objects
  2. pageMgr.showPage( "DetailsPage", "content" );
  3. pageMgr.showPage( "StatusPage", "footer" );

The name of the target area matches what was specified in the frames file. After this what you do with the frames is really up to you. The framesets are just a way of dividing up the screen real estate and after that the pages they contain are treated as though a single page was being used with all the same lifecycle methods being called.

Editing framesets

Framesets when displayed comprise multiple pages and for a visual editor such as Aria this represents a number of issues and some of these issues impinge upon the operation of the page designer. The page designer can display a page within its frameset and this is normal behavior (in fact you are given a choice of using the frameset or not when opening a page).

You need to be aware that when editing a page within a frameset you are also editing the other pages referenced by the frameset. Normally the designer works with one page, one XML file and one Java source file for the page's response methods but in the case of a frameset other pages are also open. This is evident if you manipulate components on other pages within the frameset, you will see a small red icon appear next to the page files in the files view indicating that the file is modified.

The page you opened in the designer continues to be the one being edited and if you switch to XML it is this page's XML that you will see. However if the other pages are open in different instances of the page editor you can see their XML too.

Importantly the same instance of a frameset page is used in all instances of the designer. Therefore if you edit a page in one designer and switch to another that also shows the page in a frameset you should see your changes appear in the second designer.

Stopping home page from loading

In some applications the frameset may be modified or configured at startup, and in these cases it may be undesireable to have the framework load the home page into the frameset's content area. To stop the framework loading the home page in this way set the startup property StartClass to NONE:

Specifying that no home page should be loaded

  1. StartClass=NONE

When the start class is set in this way the content area is filled with the file/class specified by the frames.xml file. If the frames file does not specify the file then the content area will start in an empty state.

Application Styles

While Aria supports framesets within what would otherwise be considered an SDI (Single Document Interface) interface the framework supports a variety of other application styles, including the MDI (Multiple Document Interface) and a Docking Framework. The frameset is therefore used to describe the composition of the application and the `target' areas of the application. The target areas are those areas, panels or windows (depening upon the application style) that can be manipulated by the page manager and the showPage method. For more information on these application styles see See Application styles..

References

For more information on layout managers see the Sun's Java documentation.

http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html

Data Binding

Data binding

Aria applications use the Model-View-Controller ( MVC) pattern to separate the key concerns in building an application. A key principal of this separation is that the model can be implemented without overdue concern for the presentation layer or user interface. Keeping the model simple makes it easy to implement business logic.

The separation of the model also means that the business logic need have little concern for the modalities or peculiarities of the user interface or the user interaction within the application.

Of course the model cannot be completely divorced from the user interface as occasionally interaction is required, say for instance if the model requires special input or needs to signal an error. Aria achieves the separation of concerns espoused by the MVC architecture by implementing a loose coupling between the data model and the user interface.

On the data or model side of the architecture Aria provides a rich data model that can be addressed via XML and XPath like references. This model can be composed of a wide variety of data and the data can be used in a variety of ways depending on the needs of the user interface and application.

Typically the data model will consist of data from a number of data sources such as static data defined in flat files, tables mapped in from a database, server side data obtained via service calls and configuration information.

Aria also saves its own data to the model, for example saving the component state data to the aria_state node so that things like list selections indices can be accessed via the mode or used for master details linking.

Your application may also save data to the model as it needs. The model grows with your application and its content is highly configurable. For example each node within the model can have multiple child nodes and each node can be a rich type, not just the basic types built into Java. Tables themselves are good examples of these composite objects, consisting of multiple rows and with rows consisting of multiple fields.

The diagram below shows how various data sources might be mapped into the hierarchy and how an individual object can be a composite of other objects. The diagram also serves to show the hierarchical arrangement of data which we will see being used in the next section on data binding.

Data binding

Aria uses a data binding technique that allows components to abstractly reference data in the model. This data can be simple scalar values or more complex types like lists and tables. From the UI perspective all that needs to be known is where the data resides in the model. Once the location has been declared Aria takes care of getting and saving the data via data binding objects which are implicitly constructed as your pages are loaded.

An example binding is shown below.

Simple data binding

  1. <Data></P>
  2. <P>
  3. <Bind name="ageCombo" source="customers/ageRanges"/></P>
  4. <P>
  5. <Bind name="firstNameEdit" source="firstname"/></P>
  6. <P>
  7. </Data>

In the above example two user interface components are bound to data. The components are named by the name attribute while the location of the data is referenced by the source attribute.

These bindings cause the named UI components to be filled with data from the source nodes whenever the page containing the components is displayed. In this specific example the ageCombo is filled with items from the customer/ageRanges node within the data model, while the firstnameEdit is filled with data from the firstname model node.

Even if the model hasn't been populated Aria will automatically create storage for the data. So, if for instance the user interface component is an input component like an edit field Aria will create a node in the model to hold the user input. In this way the model can be subsequently referenced and the user interface value can be retrieved without knowing how the input was obtained. Similarly if the bound storage is updated or modified then the user input field will display the new value whenever it is redisplayed. Furthermore, the same model node can be bound to multiple user interface components so that the same value can be displayed across multiple pages or in different contexts.

A simple static data binding

The simplest form of data that can be bound to the user interface is a static binding. A static binding is a binding that binds static or read-only data to a user interface component. Static data is often used for population of drop-down lists, labels and default values.

While it may seem like extra work to use data bindings for simple read-only data rather than hard coding the data it is often useful as the data is contained in a separate file from the source code. At its simplest this separation means that the application will be a little easier to maintain as updates to code are possible without risk to the application code (thus minimizing testing requirements).

At a more sophisticated level the separation can assist in branding of applications as the static data can be easily swapped for other data. Other features are enabled by the mapping of data used for static bindings including localization and customization of things like dimensional units, however these topics are beyond the scope of the current chapter and will be touched upon in later chapters.

Configuring the data source

So how does Aria get the data in the first place? The data source used by Aria is automatically created and configured whenever a new application is configured, but it is worth highlighting the various steps and processes used to locate the data.

Upon startup the startup.properties file of the application is accessed and the ModelData parameter is checked. This parameter points to the data model configuration file.

The data source configuration reference in the startup file

  1. ModelData=simpledatasets.xml

The configuration file is loaded by the application and processed as it contains a list of data sources. There may be one or more data sources depending on the needs of your application, but again this two stage configuration adds flexibility by allowing otherwise disparate data sources to be cleanly mapped into the data model. For examples, the specification required for database access differs considerably from the simple setup required for static data. The example below shows one such very simple configuration file.

The data source configuration file (simpledataset.xml)

  1. <?xml version="1.0" encoding="UTF-8"?></P>
  2. <P>
  3. <DataSources></P>
  4. <P>
  5. <DataSource name="SimpleData" filename="simpledata.xml"/></P>
  6. <P>
  7. </DataSources>

The file simply points of the another file which contains the static data. In a more complete application there may be several datasources of varying types and each would be listed in the data sources configuration file as a DataSource entry.

The actual data file is also relatively simple. The important attributes to note are the id attribute and the value attribute.

The static data file (simpledata.xml)

  1. <Datasets>
  2. <dataset id="customer">
  3. <data id="firstname" value="Joe"/>
  4. <data id="surname" value="Bloggs"/>
  5. <list id="ageRanges">
  6. <item value="18 - 25" id="1"/>
  7. <item value="26 - 32" id="2"/>
  8. <item value="32 - 45" id="3"/>
  9. <item value="46 - 65" id="4"/>
  10. <item value="65+" id="5"/>
  11. </list>
  12. </dataset>
  13. <dataset id="financial">
  14. <data id="carcost" value="10000.00"/>
  15. <data id="deposit" value="3000.00"/>
  16. </dataset>
  17. </Datasets>

The id field is important as it is this attribute that is used to identify a node in the model. The path used to access an individual node is just the list of all the node id s back to the root node separated by the / character. This concept should be familiar to anyone who has used a filesystem being vary like the paths that operating systems use.

As mentioned above, these configuration files are setup by Aria and Aria also provides editing facilities so you will rarely need to interact with the files directly.

The files and paths to the configuration files can also be chosen as part of the project settings.

How the bindings work

The data bindings work on the simple principle that any change made by the user to the user interface component will cause the associated model node to be updated and made consistent.

Similarly any change to the model will be propagated back to any user interface components that are bound to the model node. Aria uses some extra steps to eliminate unnecessary work and unnecessary updates but the key update occurs during page transitions and these are discussed in a later section (See Update on page display.).

When the model is updated one possibility would be to dynamically update the user interface to reflect the latest data, however this would quickly get out of hand if multiple nodes were updated. Since there is no obvious point at which we can say that an internal (to the application) is complete the update from the model side is not as aggressive as the saving of user interface data and more is left to the discretion of the programmer.

From the user interface side the bindings listen for any user input or change in state. Each binding is aware of the type of input events that affect its data and add the appropriate listeners. As these listeners are triggered they save the data to the model.

To accommodate the differences between user interface components and the types of updates they require Aria includes a range of binding types and adapters that can act as intermediaries between bindings and the various types of model node. All of these bindings share a common superclass and form a hierarchy.

Aria constructs the appropriate bindings by mapping the model data node or data source to the target user interface component. To reiterate the way in which bindings are setup In XML we again show a binding for an edit field:

A simple data binding for an edit field

  1. <Bind target="nameEdit" source="persone/name"/>

For more complex data types a one to one relationship may not exist between the data type and the user interface component. Indeed there may not be a single data binding type that can perform such binding so Aria allows chaining of data bindings and the use of adapters. For example, in the case of a type such as a table it would not be realistic to try and display a table in an edit field yet we might want to display a particular field value. With the aforementioned chaining Aria can handle such situations and fortunately it can even set up these bindings for you automatically.

The point of all of this is that in using bindings you are not constrained to using simple types or simple relationships, you can build and use a wide variety of data.

Separating data from state

We started by considering the binding of static data as read-only data. Since the data source is read-only it is not possible to save the modified data to the same node and therefore Aria creates a new node for the data by appending the source path to the ` aria_state ' node. Thus where the above example sourced its data from the ` person/name ' node the result is saved to ` aria_state/person/name '.

Some controls such as drop-down lists produce selection information (such as the index of the selected item) that do not fit well with the input data (the items it the list), particularly if the input data is considered static or read-only. Again this data is saved to the ` aria_state ' node.

For the most part you need not be aware of the output node as it is an internal detail of the bindings. In the above example the output path was not specified as Aria automatically configures the path by appending the source path to the aria_state node to give an output path of aria_state/person/name in the case of the last example.

Normally the output values and state information (e.g. the selection index in a list) associated with a data bound component are saved to the aria_state node. The output path can be specified explicitly in the binding or Aria can create it automatically by appending the source path to the path /aria_state , note however that this does not occur if the source path begins with a forward slash ( / ). Thus in the example above if the source path had been `/ person/name ', instead of ` person/name ', then the output path would be ` /person/name ' and the binding would save the user data back to the same node in the model.

Setting up a binding

Setting up a binding in XML is pretty straightforward as has been shown above. The bindings are specified in the Data section of the page:

Some more bindings

  1. <Page>
  2. ...
  3. <Data>
  4. <Bind target="firstNameEdit" source="person/firstName"/>
  5. <Bind target="secondNameEdit" source="peson/secondName"/>
  6. </Data>
  7. </Page>

While Aria setups the appropriate binding if a page is constructed from XML the story in Java is a little more complicated as the type of binding must be chosen explicitly:

Adding a binding in Java

  1. addBinding( new TextBinding( firstNameEdit, "person/firstName" ));
  2. addBinding( new TextBinding( secondNameEdit, "person/secondName" ));

In this example we know that the bound user-interface components are edit fields and therefore we use the TextBinding binding type. Some of the bindings provided by Aria are:

Some data binding types

Binding

Usage

LabelBinding

A simple binding that is used for read-only Label components

TextBinding

Bind a TextComponent to a data model value/node. The binding allows a model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by components such as Edit fields, TextComponents or TextFields

ListBinding

Bind a list to a data model value/node. The binding allows a list model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by list like components such as comboboxes or drop down lists.

StateBinding

Bind a component's state to a data model value/node. The binding allows a model node to linked to a UI component so that it can be refreshed when new data is written to the model or conversely when the UI component needs to write data to the model.

This binding is designed to be used by components such as Checkboxes.

This state change does not affect the content displayed by the component

Happily once the bindings have been constructed there is no difference between bindings constructed via Java or via XML.

Aria contains other binding types some of which support features such as localization of lists, conversion of physical dimensions and filtering of data. Please refer to the API documentation for further details.

Update on page display

Aria tries to ensure that the data visible on screen is always consistent with what is stored in the model. One of the most important times for this is during page transition. When a page is shown, its data must be updated and equally if any page had been visible its data must also be saved.

When switching pages Aria goes through a number of steps to ensure that the displayed data is consistent with the model state. The steps are as follows:

  1. The current page's data is saved by calling saveBoundComponentValues ().
  2. The current page is marked as deactivated and pageDeactivated () is called.
  3. The new page is added to the target container.
  4. The new page's bindings are updated by calling updateBindings . This method will evaluate each binding's source and output attributes and invoke any callback methods. This gives the page the opportunity to modify its bindings prior to display.
  5. The new page's values are updated by invoking the updateBoundComponentValues method
  6. The new page is marked as activated and pageActivated () is called.

You can interact with the update by implementing the pageActivated() and pageDeactivated() methods, or you can invoke the other methods to update the model at any point in the life of the application. In most cases however the data bindings take care of all the work needed to ensure the displayed data is up to date.

Saving values

As mentioned above a page's data is saved upon page transitions but this is not always sufficient and it may be necessary to explicitly save the data at some point. This can be accomplished by calling the page's saveBoundComponentValues () method. This method iterates all the data bindings on a page and updates the associated model nodes.

Updating values

Just as you may want explicit control of saving you may want to force updates to a page's data. Updates can be forced via the updateBoundComponentValues () method. This method can be invoked at just about any time and you may need to do so anytime you have a calculation of a piece of business logic that writes (or loads) data to the model.

Source and output nodes

Each data binding has a data source and optionally an output node. The role of the source node is to provide the data to be displayed by the bound component whereas the output node provides a place to save the user value or selection state.

In some cases it would not be desirable to save a user value to the same node as the input as this would either destroy the original value or modify the input dataset.

By default the output node defaults to the source node such that its path is ' aria_state/ '. Furthermore all output nodes are appended to the ' aria_state ' node.

The distinction between source and output nodes is a little gray. In some cases like a read only list it would not make sense to write selection information back to the list source so clearly in this the source and output should be maintained separate. In the case of an edit field things may not be so clear as the selection state (the caret position) is rarely of interest and one would therefore expect the input and output nodes to be the same. Aria can handle both these situations but ultimately you remain in control and can override the default behavior by explicitly naming the nodes.

Callbacks

Consider for a moment the case of reusable forms, say for example the case of a contact details form contain names, addresses and phone numbers. Such a form is pretty simple and from the above documentation you should be able to setup bindings for such a form without too much difficulty.

However, things start to get a little more interesting if the form is reused (as is often the case for something as ubiquitous as an address form). If say in the case of a financial application you had joint application for a mortgage then each person would have to fill out and address form. Now we could accomplish this by duplicating the form or by showing the form, capturing the data and then moving it to the right place but it would be a lot of work for little gain. In Aria there is another approach.

Aria supports dynamic bindings that can be updated during the life of an application. At the heart of a dynamic binding is the callback. The syntax for the callbacks is:

An embedded callback method

  1. source="${myMethod(args)}"

where myMethod is the name of a public method in the page's class. The method can be argumentless or it can have String or integer arguments. The path can also contain multiple expressions and fixed elements, for example

A callback substituting a path element

  1. source="users/${getCurrentUser()}/firstName"

where getUserName evaluates to some sort of user ID that exists (or will exist) in the model. The callback syntax is loosely based on expression language syntax and is explained more fully in See Evaluated attributes and helpers..

Dynamic bindings

Using the above technique it is possible to do things like switching users when showing user details on a page. All that needs to be done is have the getCurrentUser return a different ID and invoke the updateBindings method (which is invoked implicitly during page transitions anyhow).

Thus, as you process one applicant for the aforementioned joint mortgage application you can set the appropriate customer/user ID and then as the application process progresses you can begin the data capture process for the second user by updating the ID and redisplaying the form. Since the evaluated path has changed by the time the form is redisplayed the components on the form will be bound to different locations.

Adapters

Sometimes it is not possible to have a direct correlation between the model's data structure and a binding's use of that data. In such cases an intermediate adaptor is used. Normally the data factory takes care of the instantiation of adapters, but in some circumstances, for example when coding a specific feature in Java, it may be necessary to construct an adaptor.

The role of the adaptor is to allow the specification of bindings so that only the target component and the data source need be specified, i.e. the endpoints. The binding factory can then take care of all the rest.

Adapters are frequently used where a complex model node such as a table or list node are being mapped to a simple output type like an edit field. In most cases you need not be aware of the adapter's role in a binding but in some circumstances you may want to modify some of the adapter's properties. To do this you must use Java as the XML interface does not support such properties.

Binding tables

Using a databse table in a UI component such as a JTable is straightforward, here are some examples:

Some sample table bindings

  1. <Bind target="nameEdit" source="Voltages2" output="tables/editValue"/>
  2. <Bind target="myList" source="Voltages" output="tables/listValue" display="1" />
  3. <Bind target="myTable" source="Voltages2" output="tables/tableValue"/>

For now the output values can be ignored, they are used to save the state data of the bindings (the index of the selected record).

Selecting tables

For more complex queries it is possible to dynamically query a database, but to do this you need to use Java code and setup both the model node and the binding yourself.

A new database table can be configured as follows:

Select DISTINCT values from a table

  1. DatabaseTableModel completeVoltageTable = DatabaseTableModel.getTable( "Voltages" );
  2. completeVoltageTable.setDistinct( true );
  3. completeVoltageTable.setOrderField( "ID" );
  4. completeVoltageTable.retrieve();

Here the code starts by retrieving the basic ' Voltages ' table and then sets some additional attributes to order the table by the ' ID ' field and sets it to retrieve only the distinct rows.

The data is not pulled from the database till the retrieve method is called. It is worth noting that of course once the node is in the model you do not need to repeat the process and can instead just bind components to the node as needed.

Sometimes it is not desirable to predefine a table and in such cases a completely new database model node can be prepared in Java code.

Setup a database query

  1. DatabaseTableModel voltageTable = new DatabaseTableModel();
  2.  
  3. // Set the query elements
  4. // FROM clause, FIELDS, WHERE clause
  5. voltageTable.setupTable( "CS_VOLTAGES",
  6. "VOLTAGE_DESCRIPTION, VOLTAGE_MIN, VOLTAGE_MAX",
  7. "FREQUENCY=50" );
  8. voltageTable.retrieve();

The fields to retrieve and the where clause are specified with this approach.

As an alternative the complete SQL statement can also be specified using the setSqlStatement method.

Once the tables has been retrieved it can be used in a UI component by updating the component's bindings

Set and refresh a table control

  1. myTable.setModel( voltageTable );
  2. updateBoundComponentValues();

Linking components

To build a form using multiple UI components bound to a database table we can use the output attribute of the bindings. The output attribute is used to save the state data of the bindings.

For components such as list bindings these attributes include the selected item in the list. When displaying a list the binding will try to have the list display the last selected item (the value pointed to by the output node).

Tables and other components that use the database bindings similarly save their state to the output node. The table binding in particular saves (by default) the current row index of the table. Therefore by changing the selected row on a bound table the selected row on the underlying database table is updated. Then if the UI components bind to the same output path they should all refer to the same table row.

An example of this is the page described below:

A complete example of table usage

  1. <Page class="aria.projects.sqltables.MyTable" layout="border">
  2. <Components>
  3. <Label name="title" content="XTable Demo" alignment="center"
  4. constraint="north" style="Heading"/>
  5. <TabPanel constraint="center" >
  6. <Table name="myTable" title="first" interactive="true"
  7. headingStyle="TableHeading" style="TableData"
  8. selectionStyle="TableSelection" horizontal_scrollbar="as needed"/>
  9. <ScrollPane title="myTable2">
  10. <Table2 name="yourTable" title="second" interactive="true"
  11. headingStyle="TableHeading"
  12. style="TableData" selectionStyle="TableSelection"
  13. horizontal_scrollbar="as needed" updateModel="true"/>
  14. </ScrollPane>
  15. <Panel layout="border" title="myTables">
  16. <Combo name="myList" constraint="north" x="10" y="10" w="100" h="50"/>
  17. </Panel></P>
  18. </TD>
  19. </TR>
  20. <TR>
  21. <TD ROWSPAN="1" COLSPAN="1">
  22. <P>
  23. <Panel layout="border" title="myForm">
  24. <ScrollPane title="myTable3" constraint="center">
  25. <Table2 name="ourTable" title="second" interactive="true"
  26. headingStyle="TableHeading" style="TableData"
  27. selectionStyle="TableSelection"
  28. horizontal_scrollbar="as needed" updateModel="true"/>
  29. </ScrollPane>
  30. <Panel layout="border" title="myTables" constraint="south">
  31. <Edit name="voltageEdit" constraint="west"/>
  32. <Combo name="voltageList" constraint="center"/>
  33. </Panel>
  34. </Panel>
  35. </TabPanel></P>
  36. </TD>
  37. </TR>
  38. <TR>
  39. <TD ROWSPAN="1" COLSPAN="1">
  40. <P>
  41. <Panel layout="grid" constraint="south" rows="1" hgap="2" vgap="2" border="1">
  42. <Edit name="nameEdit"/>
  43. <Button name="prevBtn" content="prev" />
  44. <Button name="nextBtn" content="next" />
  45. <Button name="sortBtn" content="sort" />
  46. <Button name="filterBtn" content="filter" />
  47. </Panel>
  48. </Components></P>
  49. </TD>
  50. </TR>
  51. <TR>
  52. <TD ROWSPAN="1" COLSPAN="1">
  53. <P>
  54. <Events>
  55. <Event method="syncMouseSelection" target="myTable" type="MouseHandler"/>
  56. <Event method="next" target="nextBtn" type="ActionHandler"/>
  57. <Event method="prev" target="prevBtn" type="ActionHandler"/>
  58. <Event method="sort" target="sortBtn" type="ActionHandler"/>
  59. <Event method="filter" target="filterBtn" type="ActionHandler"/>
  60. <Event method="myListKey" target="myList" type="ItemHandler"/>
  61. <Event method="updateBoundComponentValues" target="ourTable"
  62. type="ListSelection"/>
  63. </Events></P>
  64. </TD>
  65. </TR>
  66. <TR>
  67. <TD ROWSPAN="1" COLSPAN="1">
  68. <P>
  69. <Data>
  70. <Bind target="nameEdit" source="Voltages2" output="tables/Voltages/>
  71. <Bind target="myList" source="Voltages" output="tables/Voltages" display="1" />
  72. <Bind target="myTable" source="Voltages2" output="tables/Voltages/mt"/>
  73. <Bind target="yourTable" source="Voltages" output="tables/Voltages/yt"/>
  74. <Bind target="ourTable" source="Voltages" output="tables/ot"/>
  75. <Bind target="voltageEdit" source="Voltages" output="tables/ot"/>
  76. <Bind target="voltageList" source="Voltages" output="tables/ot" display="1" />
  77. </Data>
  78. </Page>

In the above example three tabs are shown, on the third a table an edit field and a drop down list are all bound to the same table node and each outputs to the same path. Changing the selection on the table causes the selection on the edit field and combo box to be updated.

This update occurs because the table handles the ' ListSelection ' event and in doing so causes the ' updateBoundComponentValues ' method to be called. This method updates all the UI components bound to the model.

The output attribute specifies the path within the model to which the selection attributes are saved. These attributes include the row selection index of a table control. Normally the output of a Aria model is saved to a specific subpath in the model, the ' aria_state ' node and whenever an output path is specified it is automatically appended to this node.

In some cases it is desirable to refer to another source for this selection state. By specifying the absolute path within the model it is possible to address such paths rather than just the children of the ' aria_state ' node. In the case of a master-child setup it would be possible to link table selections using such a technique, the child table's output would be set to the master table's source path in such a scenario, e.g.

Example linking of bindings

  1. <Bind target="masterTable" source="Region" output="myRegionSelection"/>
  2. <Bind target="childTable" source="Voltages" output="/Region"/>

Finally, Aria as a Java based system is by default case sensitive. This case sensitivity also applies to database look-ups. SQL in contrast can be configured to be case in-sensitive. To help support this we allow look-up of fields in both case sensitive and case in-sensitive modes. The case sensitivity is set with a startup parameter in the startup.properties file

Flag case sensitivity

  1. CaseSensitiveDatabase=false

Advanced attribute evaluation and libraries

One of the goals Aria is to help promote an MVC architecture. The Data Binding and Event Binding helps make this clean separation by putting the UI declaration in XML, separate from the business logic which is implemented in Java. One limitation of this mechanism is that the custom logic had to be routed through event handlers in classes derived from the Page component.

This dependency on a UI component was undesirable in some cases and made it a little more difficult to implement libraries of reusable functions than we would have liked. So, as of version 2.0 we have extended the attribute and event bindings to solve this problem.

Evaluated attributes

Attributes within a Aria page can be specified dynamically, for example

Basic attribute evaluation

  1. <Button name="DecBtn" x="178" y="43" w="42" h="20" content="${getContent()}"/>

The code ${getContent()} is an expression that is evaluated at runtime each time the expression is encountered. For a page component declaration the expression is evaluated when the page is loaded but expressions can be used in other locations such as within the data model, the data bindings, the validations or the event bindings.

An evaluated attribute's implementing method is by default in the owner page such that a reference like ${myMethod()}, which would evaluate to a method in the current page with a signature like:

Method signature

public void myMethod();

In Aria 2.0 The attributes can also be defined in classes other than the current page or classes derived from Page. The syntax for such expressions is as follows:

Extended attribute declarations

Syntax

Behavior

${mypackage.MyClass.myMethod(args...)}

to invoke a static method

${mypackage.MyClass[].myMethod(args...)}

to create a new instance of the class on each evaluation

${mypackage.MyClass[referenceName].myMethod(args...)}

for a named object instance

${myMethod[referenceName](args...)}

for a method contained within the invoking page

${[referenceName].myMethod(args...)}

for a method contained within the class instance referred to by the reference name.

where mypackage is the name of the Java package containing the class MyClass . The value of referenceName is a user defined value that identifies the instance of the class. The application instantiates an instance of the class when the expression is first encountered and thereafter maintains the instance with each subsequent call retrieving the same instance of the class. As in early versions, the method call can also contain zero or more arguments.

What this means in practice is that the class or classes implementing an applications business logic no longer need be derived from Page. In this way it is possible to build libraries of reusable functions. Lets look at an example:

Sample extended attributes

  1. <Page class="com.mypackage.ui.swing.SimpleCalculator">
  2. <Components>
  3. <Edit name="Counter" ... />
  4. <Panel ...>
  5. <Button name="DecBtn" ... content="${org.formaria.library.Calculator.getDecLabel()}"/>
  6. <Button name="ClearBtn" content="${org.formaria.library.Calculator[foo].getClearLabel()}"/>
  7. <Button name="IncBtn" ... content="${[foo].getIncLabel()}"/>
  8. <Button name="divideBtn" ... content="/"/>
  9. <Button name="multiplyBtn" ... content="X"/>
  10. </Panel>
  11. </Components>
  12. <Events>
  13. <Event method="doEquals" target="ClearBtn" type="ActionHandler"/>
  14. <Event method="org.formaria.library.Calculator.increment"
  15. target="IncBtn" type="ActionHandler"/>
  16. <Event method="org.formaria.library.Calculator[].decrement"
  17. target="DecBtn" type="ActionHandler"/>
  18. <Event method="org.formaria.library.Calculator[foo].divide"
  19. target="divideBtn" type="ActionHandler"/>
  20. <Event method="[foo].multiply" target="multiplyBtn" type="ActionHandler"/>
  21. </Events>
  22. </Page>

In the above example we are recreating the simple calculator that we have used in other examples. While the example is a little contrived (we will document some more realistic examples later) it shows the new syntax in action.

Line 5: Shows something similar to the pre Aria 2.0 syntax (which is still valid) except that the handler method is now in a separate class ( org.formaria.library.Calculator ) and in a separate package to the page ( com.mypackage.ui.swing.SimpleCalculator ) class. The method being invoked is a static member of the class.

Line 6: Shows a similar call except that instead of a call to a static method a concrete instance of the class is constructed. Once the instance of the class is constructed it is labelled for subsequent use.

Line 7: Reuses the reference setup it line 6 and invokes a different method on the same object. The reference is project wide so the object associated with the name can also be used across pages.

Expression evaluators

The expressions are evaluated by an ExpressionEvaluator and each page has by default its own instance of the default expression evaluator. (The default evaluator delegates storage of the referenced classes to the project). However, the page allows this evaluator to be replaced and a different evaluator can be inserted. This replaceable evaluator allows a route to include other expression evaluators such as interpreters. As an example an evaluator for the Groovy language has been created.

Using evaluated attributes

OK, so we have seen how the attributes of an XML file can in fact be callbacks to methods in your page class or in some other class. What does this mean for the application?

Essentially this means that the model is dynamic, it can be adapted to meet the changing needs of your application as a session progresses. You are not restricted to the setup encoded in the XML at start-up.

The dynamic model also means the data structure specified in the XML (or in your code for that matter) can be mapped from one instance to another via the evaluated attributes and callbacks. We have already seen how this could be use with something as simple as an address form.

Evaluated attributes also make it possible to apply more advanced techniques like filtering data and providing access control. Once you get to grips with the basic functionality you should find the use of evaluated attributes a very powerful mechanism.

Registration of custom data bindings

Since Aria supports an open ended number of compenent types and a large variety of data objects it is often useful to create reusable data bindings for these situations. Prior to version 3.0 it was possible to add custom data bindings, but the mechanism employed suffered from some limitations. Therefore, to deal with these limitations and to provide a more consistent regsitration mechanism the data bindings registration mechanism has been completely revised.

Adding a new binding type

The data binding mechanism in Aria shares some common features with the other configuration file systems in Aria and Aria. The system allows multiple configurations to be added to an application so that various modules can contribute support to an application, and in turn the factory classes iterate the configurations till suitable matching resources are found.

The matching strategies used with the configuration files allow various levels of refinement so that a broad concept like an interface can be matched or a very specific class instance can also be matched.

Adding a configuration file

The first step in employing the data bindings is to add a registration file for the bindings. By default Aria includes and adds a bindings.xml file for the built in bindings, and it is instructive to look at this file:

The built-in binding registration

  1. <Bindings>
  2. <InspectorBindings>
  3. </InspectorBindings>
  4.  
  5. <ClassBindings>
  6. <Binding target="org.formaria.*.Button"
  7. class="org.formaria.aria.data.TextBinding" type=""/>
  8. <Binding target="org.formaria.swing.RadioButton"
  9. class="org.formaria.aria.data.RadioBinding" type=""/>
  10. <Binding target="org.formaria.swing.RadioButton"
  11. class="org.formaria.aria.data.TextBinding" type="text"/>
  12. <Binding target="org.formaria.swing.RadioButton"
  13. class="org.formaria.aria.data.StateBinding" type="state"/></P>
  14.  
  15. <Binding target="org.formaria.swing.Checkbox"
  16. class="org.formaria.aria.data.StateBinding" type=""/>
  17. <Binding target="org.formaria.swing.Checkbox"
  18. class="org.formaria.aria.data.TextBinding" type="text"/>
  19. <Binding target="org.formaria.swing.Tree"
  20. class="org.formaria.swing.tree.TreeBinding" type=""/>
  21.  
  22. <!-- TODO add implementation of this error handling mechanism-->
  23. <!-- Binding target="org.formaria.*.Table">
  24. <onError fixError="DataSourceClassMissing"/>
  25. </Binding-->
  26. </ClassBindings></P>
  27.  
  28. <InterfaceBindings>
  29. <Binding target="org.formaria.aria.ListHolder"
  30. class="org.formaria.aria.data.ListBinding" type=""/>
  31. <Binding target="org.formaria.aria.ListHolder"
  32. class="org.formaria.aria.data.ListBinding" type=""/>
  33. <Binding target="org.formaria.aria.TextHolder"
  34. class="org.formaria.aria.data.TextBinding" type=""/>
  35.  
  36. <!-- TODO add implementation of this error handling mechanism-->
  37. <!-- This does not actually use a binding -->
  38. <!--Binding target="org.formaria.aria.ModelHolder">
  39. <onError fixError="DataSourceClassMissing"/>
  40. </Binding-->
  41. </InterfaceBindings>
  42.  
  43. <InstanceBindings>
  44. </InstanceBindings>
  45. </Bindings>

An application can add an application specific binding by including a bindings.xml file in its classpath. The name of the application bindings file can be changed by specifying the BindingsRegistry startup parameter.

An individual module or application can also add as many configuration files as it needs. For the data bindings the configurations are added to the RegisteredDataBindingFactory via the addConfigFile method.

The configuration file format

The matching strategies used with the configuration files allow various levels of refinement so that a broad concept like an interface can be matched or a very specific class instance can also be matched. The matching modes are as follows

 

 

 

InspectorBindings

Uses a custom/user defined Inspector class to identify the appropriate binding. The inspector must implement the Inspector interface via the inspector attribute.

InstanceBindings

Match a class to an instance of the specified class

InterfaceBindings

Matches to a class that implements the specified interface.

ClassBinding

Match a binding target to a instance of the specified class, or a subclass of the specified class.

Each of these binding types should specify the target, class and type attributes, but the registration may also specify addition attributes and these are available to the data binding once the XDataBinding.setup method is invoked.

Within a particular mode of bindings the bindings registrations are checked in the order in which they are registered and specified.

Specifying an InspectorBinding

As mentioned above the Inspector Bindings allow you to create bindings that can inspect the comtents and type of the source data and the type and instance of the target component. Using a special Inspector interface you can create your own inspector and thereby gain fine grained control of how a binding is used and applied.

The Inspector interface

  1. /**
  2.   * Called by the registration factory for InspectorRegistration instances
  3.   * to allow this matcher to arbitarate as to whether or not the component
  4.   * can be used with the particular registration object.
  5.   * @param comp the component involved in the drag oepration
  6.   * @param regConfig a table of registration attributes
  7.   * @param instConfig a table of attributes used in the instance
  8.   * declaration
  9.   * @return true if the handler matches, otherwise false
  10.   */
  11. public boolean inspect( Object comp, Hashtable regConfig,
  12. Hashtable instConfig );

Most bindings won't do very much with the parameters and instead rely on the setupHelper method to set the source and output data models to their initial state. An example of this is the TextBinding

TextBinding setup

  1. public void setup( Project project,
  2. Object c,
  3. Hashtable bindingConfig,
  4. Hashtable instanceConfig )
  5. {
  6. setupHelper( project, c, bindingConfig, instanceConfig );
  7. attribStr = (String)instanceConfig.get( "attrib" );
  8. }

Data binding adapter

Many data models nodes are not immediately usable in a binding, lacking the the form, structure, or the interface needed by the binding. An example of this is the ListBinding which uses the ListModelAdapter to adapt data source such as tables to lists. A list may display just one column of a table or a single attribute of a model node and the adapter adapts the model node to the requirements of user component. Various adapters are built into the Aria framework. The adapter is specified with the 'adapter' attribute of the binding instance for those bidning types that use adapters.

Data binding lifecycle

Once the data binding registration has been matched, an instance of that binding is created and the setup method is called is as described above. The setup method usually establishes all the data and resources need by the binding.

Data binding contexts and containers

The binding is maintained within the DataBindingContext owned by the page. As the page state changes with page navigation the data binding context requests that the binding saves its data by calling the set method and when the page is displayed the context calls the get method, requesting that the binding updates the UI component.

The application may also explicitly update the individual bindings using the updateBinding method, or via the updateBindings or updateBoundComponentValues methods.

Reevaluation of registered binding types

The updateBindings method differs from the updateBoundComponentValues method in that it causes a reevaluation of the binding source and output model specification. The reevaluation is often used if the model node specified hass changed, or if the binding paths include evaluated attributes.

Evaluated attributes are references to methods of the owning page (or sometimes library objects), that allow applications to control and manipulate the model paths used by the bindings. In this way the application can redirect the bindings to different parts of the model. Sometimes this redirection is used to do thing like adapting a page to the data of one object or another, for example the data belonging to one user or another.

One important aspect of the registration facility is that the basic data bindings is that the bindings do not store the tables of binding instance and binding registration attributes, so if these attributes are needed by a binding then it is the responsibility of the individual binding class to store the necessary data.

Using the ModelHelper

The ModelHelper class provides a number of methods designed to facilitate access to the data model and avoid the verbose type casting that is sometimes required.

The class can be used with a specific instance of a model node by instantiating a new instance of the helper, passing the target node as an argument to the constructor. However the most common use is via the static methods, for example:

Sample usage of the ModelHelper

  1. // Find a node and get its value
  2. String myValue = ModelHelper.getString( project, rootModel, "as/you/were" );
  3.  
  4. // Get an integer value from a specifc node
  5. int size = ModelHelper.getIntValue( modelNodes[ i ], "size" );</P>
  6. // Get a double or a default
  7. double velocity = ModelHelper.getDouble( carModel, "trabant/speed", 30.0 );
  8. double velocity = ModelHelper.getDouble( carModel, "ferrari/speed", 220.0 );

The ModelHelper is just one helper class in the Aria and Aria frameworks. If you see things like lots of casting or repetitive code look for a helper, there may already be one included in the framework or on the Aria forums. If you don't find what you are looking for write your own, they are not complicated and can save you lots of tedious casting!

Event Handling

Aria aims to simplify application development by reducing the amount of boilerplate code that must be generated for an application. One of the most time consuming activities and a troublesome feature of application development is the wiring together of components and events.

Java's abstract event handling mechanism is certainly flexible but more often than not the code needed for even simple event handling is repetitive and adds little of particular value to the application. In Aria the most common instances of this event handling wiring is taken care of by basic library functions so that all the Aria user need do is specify what is specific to a particular event.

Aria's event handling relies on the basic Java event handling mechanisms and at any point you can go beyond the Aria library and work directly with the low level event mechanisms. This chapter shows how to work with both mechanisms.

Adding EventHandlers interactively

Adding event handling in Aria is very easy. Simply select the component whose event you want to handle within the page designer. Then in the properties palette locate the event of interest. In the screen shot below you can see a mouse event being entered for the selected button.

Once the name has been entered and you press enter Aria finds the event handler in the source code or adds a new event handler if the handler doesn't exist already.

To correctly add the handler Aria must do a few extra things that may require additional input or modify the source code in other ways. Of these tasks the most obvious occurs if no name has been given to the component being edited then a popup dialog will appear requesting that you enter a name for the component.

Once a name is available for the component Aria can proceed and opens the source code in a text editor at the new event handler method.

If necessary a new source file may have been created into which the handler is added. The event is bound to the user interface and the new Java class by a declaration in the page's XML. You can see the event declaration by switching to the page designer and selecting the XML button in the designer's toolbar..

The page XML is updated as soon as the new method is added and the XML text editor is displayed.

And that's it, all of the infrastructure for event handling is taken care of and you can begin entering the application specific business logic.

Basic event handling

Regardless of how you edit the event handlers for your application the basic mechanisms are the same. The page's XML declaration names the event to be used to respond to a particular event type for a given component.

While the XML could point to a method in the page's base class or some existing utility class, most pages involve some form of custom class for handling the custom logic that belongs to the page. Pages can derive directly from Page but by changing the base class you can customize the event handling for components

This example re-introduces some Java code. Now the base class is our own custom written class derived from Page so that calls specified in the XML can be intercepted by the custom class.

A new Java class declaration

  1. public class EventHandling extends Page

The XML file now specifies this custom class as it's base class and defines the two buttons which will have events attached.

A page specifying events

  1. <Page class="org.formaria.samples.xmlbuild.EventHandling">
  2. <Components>
  3. <Component name="btnProceed" type="XBUTTON" x="40" y="10" w="60" h="20" content="Proceed"/>
  4. <Component name="btnCancel" type="XBUTTON" x="110" y="10" w="60" h="20" content="Proceed"/>
  5. </Components>
  6.  
  7. <Events>
  8. <Event method="nextPage" target="btnProceed" type="MouseHandler" next="xmlnavsamplescr2"/>
  9. <Event method="cancel" target="btnCancel" type="MouseHandler"/>
  10. </Events>

So now the java code handling the events is as follows:

The Java class's implementation of the events

  1. public void nextPage()
  2. {
  3. if ( wasMouseClicked() ){
  4. // Do something here...
  5. PageManager.showPage( getAttribute( "next" ));
  6. }
  7. }
  8.  
  9. public void cancel()
  10. {
  11. if ( wasMouseClicked() ){
  12. Button button = (Button)findComponent( "btnCancel" );
  13. button.setText( "Clicked!" );
  14. }
  15. }

Built-in event handlers

Aria supports several basic types event, the support types are described below:

Various event handler types

Type

Usage

ActionHandler

Action handlers respond to ActionEvents such as button clicks, selections, edit completions and so on. This is a simple interface with just a single event.

MouseHandler

Mouse events comprise an number of events including mouse entry, exit and changes of state including when the mouse is pressed, released and clicked. It is normal to check the type of event that trigger the call to a mouse handler method unless you have some generic action for all the above events.

MouseMotionHandler

Like the above mouse handler the mouse motion handler responds to more than one mouse event, namely the mouse moved and mouse dragged events. It is unusual to interact with this type of handler.

ItemHandler

This type of handler is invoked whenever the state of a component changes, for example the selection state of a radio button or a check box.

KeyHandler

This type of handler is used for handling keyboard event including key pressed, released and typed. Sometimes this type of handler is used to help enforce validation rules on input fields.

FocusHandler

A change of focus will trigger this handler both when a component gains focus and when it looses focus.

MenuHandler

Invoked whenever a menu item is selected.

TextHandler

Invoked whenever a text value has changed.

All of these basic event handlers are added via the Page class or its derivatives. We have already seen an example above of adding a handler via XML but the handlers can also be added via Java with just as much ease. Here's an example that is equivalent to the above XML:

Adding an event handler with Java

  1. addMouseHandler( btnProceed, "nextPage" );
  2. addMouseHandler( btnCancel, "cancel" );

Accessing event objects

In the case of multiple event being processed by a single event handler it is sometimes useful to query the properties of the event that caused the handler to be invoked. This is frequently the case with mouse events and key events (and special helper methods are defined for working with mouse events).

To access the event that triggered the handler it is necessary to call the getCurrentEvent method. The event can then be cast to whatever the appropriate type and the event specific data is then accessible. The example below shows how information is extracted from a mouse event. Note that the event is first checked to ensure that it is a mouse event before attempting to cast the value.

Accessing the current event

  1. public void handleMouseClicks()
  2. {
  3. if ( wasMouseClicked()) {
  4. MouseEvent me = (MouseEvent)getCurrentEvent();
  5. Point pt = me.getPoint();
  6. statusLabel.setText( "The mouse is at: + Integer.toString(pt.x) + "," +
  7. Integer.toString(pt.y));
  8. }
  9. }

Under the hood

In adding event handlers Aria has to go through a number of steps and it is worth understanding these steps, if only for the sake of in-depth knowledge.

Consider first of all the process of adding the event handler. Aria constructs your page and the user interface for the page. This is really a process of adding the user interface components to an instance of your page's class. That class is derived from Page which includes the event handling mechanism. In processing the event declaration in your page's XML Aria therefore uses the page's event handling mechanism.

Like most normal Java code Aria pages respond to event through the standard listener interfaces and to save coding the page implements a number of the more common listener interfaces. The page can therefore listen for the common events associated with these interfaces. One of the most common is the ActionListener interface which is invoked in response to an event such as a button click. So when you ask a Aria page to add an actionHandler for a button it justs adds itself as an ActionListener for the button.

Once the button is clicked the page is notified through the interface. The method you registered is then looked-up and invoked via reflection (since Aria doesn't really have any knowledge of your method). No great magic there, but the process saves some more of the plumbing code.

Custom event handlers

It is possible to add support for just about any event type to a Aria application. The EventAdapter interface is an interface that wraps an object implementing the particular listener of interest. The wrapper is intended to be generic and therefore delegates most of its work to the Aria event handler.

Adding a custom event handler

  1. <Event method="tabChanged"
  2. target="tabPanel"
  3. type="org.formaria.events.swing.ChangeEventHelper"/>

The ChangeEventHelper implements the ChangeListener interface and in responding to ChangeEvents it invokes the named method. The framework assumes that the class referenced is an instance of the EventAdapter interface, which specifies the parameters needed to create and add the event handler.

The complete source for the above helper is shown below

ChangeEventHelper, a custom event handler

  1. package org.formaria.events.swing;
  2.  
  3. import javax.swing.event.ChangeEvent;
  4. import javax.swing.event.ChangeListener;
  5. import org.formaria.aria.events.EventAdapter;
  6. import org.formaria.aria.events.AriaEventHandler;
  7.  
  8. /**
  9.   * A helper for the Swing ChangeListener interface
  10.   * <p>Copyright (c) Formaria Ltd., 2009
  11.   * <p>License: see license.txt
  12.   * $Revision: 1.2 $
  13.   */
  14. public class ChangeEventHelper implements ChangeListener, XEventAdapter
  15. {
  16. protected AriaEventHandler eventHandler;
  17.  
  18. /**
  19.   * Set the current event handler
  20.   * @param xeh the event handler
  21.   */
  22. public void setEventHandler( AriaEventHandler xeh )
  23. {
  24. eventHandler = xeh;
  25. }
  26.  
  27. /**
  28.   * Get the name of the adder method e.g. addActionListener
  29.   * @return the method name
  30.   */
  31. public String getAddedMethodName()
  32. {
  33. return "addChangeListener";
  34. }
  35.  
  36. /**
  37.   * Get the name of the listener e.g. java.awt.event.ActionListener
  38.   * @return the listener name
  39.   */
  40. public String getListenerInterfaceName()
  41. {
  42. return "javax.swing.event.ChangeListener";
  43. }
  44.  
  45. /**
  46.   * Get the event mask
  47.   * @return the mask e.g. AWTEvent.ACTION_MASK
  48.   */
  49. public long getEventMask()
  50. {
  51. return 0x100000;
  52. }
  53.  
  54. /**
  55.   * The change event has occured
  56.   */
  57. public void stateChanged( ChangeEvent e )
  58. {
  59. try {
  60. eventHandler.invoke( getEventMask(), e );
  61. }
  62. catch ( Exception ex ) {
  63. ex.printStackTrace();
  64. }
  65. }
  66. }
  67.  

In the case of the above class the listener specific code is the stateChanged method, but all that does is delegate to the event handler.

Exception handling

Any application logic relying on user input is susceptible to a variety of errors and exceptional conditions that make process impossible to complete. In the Java world it is normal to trap these exceptions as they occur.

In Aria exception handling is closely tied to the event handling mechanism and the validation mechanism. The idea is that exceptions and validation errors can be handled in a manner similar to the event handling mechanism. Furthermore exceptions can be handled with a certain degree of separation from the normal flow of events since, as the name suggests they occur in exceptional circumstances.

For example an incomplete form could potentially produce a series of exceptions during event processing and during validation. From a user point of view it is unlikely that individual warnings would be produced for each error and instead the user would be informed of a set of errors or a synopsis of the errors. Aria supports this type of error handling and also redirection of errors to a central error handler. You can find more information on exception handling and input validation at See Writing a custom exception handler.

Generic event handling

Aria has built in handlers for the event types listed above however in some cases it is not adequate for all demands or it may be necessary to handle some other event types. Therefore the Aria API includes a number of methods to assist in integration of other event types.

To customize the event handling it is necessary to subclass the Page class. In subclassing Page there are three possible mechanisms for changing the event handling.

First the setEventHandler method can be used to set a specific event handler. Changing the event handler gives considerable scope to change the behavior of the event handling within Aria.

Secondly individual events can be added with the methods listed below. Choosing to add or modify specific events means that the changes will have less impact than modifying the event handler but at the cost of more work if the changes are to be reused elsewhere.

Finally the underlying Java event handling mechanisms can be used to handle events just as would be the case in a plain old Java application. Nothing in Aria should interfere with the basic event handling mechanism.

Event Handler API

  1. public void setEventHandler( EventHandler eh );
  2.  
  3. public void addListener( Object comp, String listenerName, String argType );
  4. public void addHandler( Object comp, long eventType, String methodName ) throws Exception;

The first method setEventHandler sets the event handler class for the page. This method should be called in the page constructor. The page delegates to the event handler whenever an attempt is made to add a new event handler.

Most applications call specific methods in the page's API such asaddActionHandler, which in turn delegates to the event handler instance. The event handler is essentially a helper class that takes care of adding the event listener (by calling addListener and addHandler) and mapping the listener to a response method in the page. Further information on these APIs can be found in the API documentation.

Under the hood the Page class implements the methods specified by the ActionListener interface. In adding an event the page adds itself as a listener to the target component. When the event of interest is fired the page therefore responds to the event via this listener interface. The page then searches for the handler (that you had registered) and invokes it.

Extended Event Specification

As we saw in the earlier data binding chapter the syntax for dynamic method bindings has been extended to include referencing of classes other than the class upon which the current page is based.

The obvious difference from the data binding syntax is that an event binding sets up a reference to a method (the event handler method) and does not itself cause the method to be evaluated. The syntax for the extended event bindings is as follows:

Event specifications

Syntax

Role

mypackage.MyClass.myMethod

to invoke a static method

mypackage.MyClass[].myMethod

to create a new instance of the class on each evaluation

mypackage.MyClass[referenceName].myMethod

for a named object instance

myMethod[referenceName]

for a method contained with the invoking page

[referenceName].myMethod

for a method contained with the class instance referred to by the reference name.

To illustrate this extended event specification syntax let's have a look at an example.

Counter example extended with library functions

  1. <Page class="com.mypackage.ui.swing.SimpleCalculator">
  2. <Components>
  3. <Edit name="Counter" ... />
  4. <Panel ...>
  5. <Button name="DecBtn" ... content="${org.formaria.library.Calculator.getDecLabel()}"/>
  6. <Button name="ClearBtn" content="${org.formaria.library.Calculator[foo].getClearLabel()}"/>
  7. <Button name="IncBtn" ... content="${[foo].getIncLabel()}"/>
  8. <Button name="divideBtn" ... content="/"/>
  9. <Button name="multiplyBtn" ... content="X"/>
  10. </Panel>
  11. </Components>
  12. <Events>
  13. <Event method="doEquals" target="ClearBtn" type="ActionHandler"/>
  14. <Event method="org.formaria.library.Calculator.increment"
  15. target="IncBtn" type="ActionHandler"/>
  16. <Event method="org.formaria.library.Calculator[].decrement"
  17. target="DecBtn" type="ActionHandler"/>
  18. <Event method="org.formaria.library.Calculator[foo].divide"
  19. target="divideBtn" type="ActionHandler"/>
  20. <Event method="[foo].multiply" target="multiplyBtn" type="ActionHandler"/>
  21. </Events>
  22. </Page>

In the above example we are recreating the simple calculator that we have used in other examples. While the example is a contrived (we will document some more realistic examples later) it shows the new syntax in action.

Line 14 sets up an event binding to a static member of the org.formaria.library. Calculator class

Line 15 constructs a new instance of the class each time the event is bound (when the XML expression is loaded and evaluated).

Line 16 constructs a new instance and sets up a reference to the new instance. If the instance already existed it would be reused. In this case a instance of 'foo' had already been created at line 7 so that instance is reused instead of creating a new instance.

Lines 17 and 18 create bindings to other methods in the class and as in the case of line 16 they each use the 'foo' object created on line 7.

The importance of this simple extension should not be under estimated. Quite simply this extension means that your event handlers do not necessarily have to exist in a class derived from Page. In fact the extension means that you can build libraries of utility functions for common tasks and these functions can be used for things like navigation, calculations and other tasks that do not have to be tied to a particular page.

It is also worth noting that this extension mechanism does not mean that the library functions built using the mechanism cannot access the current page or the current component. Such values can be passed as arguments to the function are they can be accessed via the project, the page, the current event and the component hierarchy.

Adding event types

The EventHandler class loads a file events.xml to get a specification of the events it is to handle. The event handler can be instructed to handle other event types by adding additional configuration files. The easiest way to do this on a per project basis is to add an events.xml file to the project's resources. A startup parameter EventHandlers controls the name of the file loaded, but if none is found Aria attempts to load events.xml.

The default events.xml file is as follows:

Default event specification

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <events>
  3. <event name="ActionHandler" interface="java.awt.event.ActionListener" mask="128"/>
  4. <!-- mask="AWTEvent.ACTION_EVENT_MASK"/-->
  5. <event name="MenuHandler" interface="java.awt.event.ActionListener" mask="128"/>
  6. <!-- mask="AWTEvent.ACTION_EVENT_MASK"/-->
  7. <event name="FocusHandler" interface="java.awt.event.FocusListener" mask="4"/>
  8. <!-- mask="AWTEvent.FOCUS_EVENT_MASK"/-->
  9. <event name="TextHandler" interface="java.awt.event.TextListener" mask="1024"/>
  10. <!-- mask="AWTEvent.TEXT_EVENT_MASK"/-->
  11. <event name="ItemHandler" interface="java.awt.event.ItemListener" mask="512"/>
  12. <!-- mask="AWTEvent.ITEM_EVENT_MASK"/-->
  13. <event name="KeyHandler" interface="java.awt.event.KeyListener" mask="8"/>
  14. <!-- mask="AWTEvent.KEY_EVENT_MASK"/-->
  15. <event name="MouseHandler" interface="java.awt.event.MouseListener" mask="16"/>
  16. <!-- mask="AWTEvent.MOUSE_EVENT_MASK"/-->
  17. <event name="MouseMotionHandler" interface="java.awt.event.MouseMotionListener" mask="32"/>
  18. <!-- mask="AWTEvent.MOUSE_MOTION_EVENT_MASK"/-->
  19. </events>

Navigation and Page Management

Aria provides all the infrastructure necessary to manage pages within an application whether the pages are created with XML or via Java. The page manager at the heart of Aria also provides support for framesets and page history management.

The page management function is separated from the application and from the individual pages to provide independence of the underlying widget set.

Navigation

In Aria each page can be regarded as a discrete entity displayed by the application or applet. All pages must be derived from the basic Page class and this class interacts with the PageManager class to provide several methods of changing the displayed page. The page manager acts to maintain references to pages so that they do not need to be recreated whenever they are displayed, hidden or redisplayed.

In order to get a reference to the PageManager for the current project the call below can be used.

Retrieving the PageManager for the current project

  1. ProjectManager.getPageManager();

The main methods are showPage and showPrevious , which unsurprisingly show a new page and redisplay the previously selected page.

To invoke these methods it is necessary to add a way of initiating the navigation action. Normally this is accomplished by having a button or hostpot on the page with a text or graphic to indicate that a click will cause, say the next page to be displayed.

In the example below the user can initiate the navigation action by clicking on a button.

Setting up the navigation button and response event

  1. <Page class="myPackage.MyCustomPageClass">
  2. <Components>
  3. ...
  4. <Button name="btnProceed" x="40" y="10" w="60" h="20" content="Proceed"/>
  5. </Components>
  6. <Events>
  7. ...
  8. <Event method="proceed" target="btnProceed" type="ActionHandler"/>
  9. </Events>

The page must then implement the proceed response method. The Page class contains a reference to the PageManager for the current Project and can be used directly from subclassed Pages as shown below:

Implementing the response method

  1. package myPackage;
  2.  
  3. import org.formaria.aria.*;
  4. import org.formaria.swing.*;
  5. import org.formaria.aria.data.*;
  6. import org.formaria.aria.helper.*;
  7.  
  8. public class MyCustomPageClass extends Page
  9. {
  10.  
  11. public MyCustomPageClass()
  12. {
  13. }
  14.  
  15. ...
  16.  
  17. public void proceed()
  18. {
  19. pageMgr.showPage( "Page2");
  20. }
  21. }
  22.  

The page manager

The Aria page manager provides a mechanism for loading and displaying pages. It takes care of managing the page's lifecycle so that you need not be concerned with when and how a page is loaded or destroyed.

When an application requests a page by calling the showPage method the actual loading of the page is delegated to the page manager. The page manager will then attempt to load an XML file corresponding by name to the requested page. If an XML file is not found then the page manager will attempt to load a Java class of that name (and failing that, a dummy page can be created).

The page manager also takes care of the page lifecycle, calling the pageCreated, pageActivated and pageDeactivated methods as appropriate.

As the pages are separated from one another and from the applet class through this page manager mechanism the pages can be loaded independently of each other (unless explicit links or references are inserted). Furthermore, as the page manager is also separate from the applet and the widget set it is possible to code a large variety of utility functions so that they can work with different widget sets such as Swing and AWT.

Showing a page

A few simple methods are provided to allow loading and display of pages. It is generally assumed that all page handling will be delegated to the page manager and this is the case whenever the Page 's methods are used to display pages and this is the case for the normal response methods used in Aria applications.

The showPage method simply takes the name of a page. An alternative form of the showPage method can also take a second parameter indicating the target area (but more on this later). The showPage method loads the page as described above and requests that the applet displays the page. Implicitly in this process any currently visible page is deactivated and hidden.

Once a page has been shown it is regarded as the current page and at any point the current page can be referenced by calling the page manager's getCurrentPage method.

The showPage call can also include a URL so that the page can be loaded from a remote source. In fact most resource specifications in Aria can include URLs in this way.

Targets

Aria supports the notion of framesets similar to those used in HTML. Like HTML the framesets in Aria are defined in a separate frames file (pointed to by the startup properties file).

Each of the areas referenced by the frames file is known as a target area. Most of the page management methods include versions that take a target area name as a parameter and these functions affect the named area. Thus if you use the showPage method with a target area name, the area named in the method call is updated instead of the application's entire content area.

In cases where a frameset is used it is still possible to call the single argument versions of the page management functions. It is then assumed that the target area name defaults to ` content'.

Generally this default name works well where the frameset is being used to provide navigation facilities and other long lived facilities such as banners and footer. (see also Framesets.)

Page history

In addition to the showPage method Aria also provides the showPrevious method. As a page is shown, the history/order of the displayed pages is saved and the showPrevious method allows you to reverse through this order of pages. The showPrevious method works in much the same way as showPage and in fact relies on the same mechanisms to update the application.

Page history should be used with caution as it does not necessarily correspond to the end user's notion of what the previous page was. Furthermore if the user has navigated through a loop of pages then over use of the showPrevious method could lead to confusion.

Frequently there may be a need for multiple navigation mechanisms to help avoid such situations. It may be that it is just the user's perception of the order of pages that is out of sync with the way things are implemented and therefore we recommend adding visual feedback to your application to help support navigation.

Visual feedback should let the user know where they are within the application, how far they have progressed and how far they have to go to completion. The type of feedback used will of course vary from application to application and will also depend on the visual styles employed but whatever the detail, such hints greatly aid usability.

Resetting the page cache

On rare occasions it may be necessary to reset the page cache and page history. Some instances of this occur whenever the bound data is changed or reloaded from a different source, when a language changes or when it is necessary to reset all pages, removing user inputs.

To reset the page cache in this way use the following code:

Reset the page cache

  1. // Use the Page's member variable pageMgr!
  2. pageMgr.reset();

Page loaders

Aria employs an extensible method of loading pages. The page manager normally loads pages from XML or from Java classes if it cannot find a suitable XML page. This process can be altered by setting up secondary page loaders. The page loader is described by the PageLoader interface and can be configured through the startup property value for BuilderClass and NumBuilderClasses. Aria includes several page loaders including Aria's default page loader, an HTML page loader (see Importing HTML.) and a generic page loader for loading forms and other file formats (see Importing forms.).

Dataset defined navigation

When applications which use pages in a particular sequence are being written such as application forms consisting of multiple pages or wizard applications it is possible to define those pages in a static XML dataset file. For example in the code sample below, pages are defined within in a dataset file for a wizard type application.

  1. <Datasets>
  2. <dataset id="navigation">
  3. <dataset id="wizardpages">
  4. <data value="welcome"/>
  5. <data value="projectinit" desc="WIZ_PROJ_SET"/>
  6. <data value="languagepage" desc="WIZ_PROJ_LANG"/>
  7. <data value="colourscheme" desc="WIZ_PROJ_LAYOUT"/>
  8. <data value="finish" desc="WIZ_PROJ_FINISH"/>
  9. </dataset>
  10. </dataset>
  11. </Datasets>
  12.  

This dataset file needs to be referenced from the datasources file which is in turn loaded from the ModelData startup property. For more on loading datasets see See Data management.. Once loaded, the application will have access to these definitions via the DataModel for the project. There are three possible paths declared in this wizard dataset, `wizardpages', `generationpages' and `buildpages'. The relevant path can be set upon clicking a button, a menu or seleting a radio button.

A singleton class can then be written which takes care of the navigation and of course the pages referenced by the value attribute of the model nodes need to exist on the classpath.

NavigationManager Singleton class

  1. public class NavigationManager {
  2.  
  3. BaseModel navMdl;
  4. protected int currentNode = 0;
  5. BasePage currentPage;
  6. private static NavigationManager navMgr;
  7.  
  8. private NavigationManager( String navPath ) {
  9. navMdl = ( BaseModel ) ProjectManager.getModel().get( "navigation/wizardpages" );
  10. }
  11.  
  12. public static NavigationManager getInstance()
  13. {
  14. if ( navMgr == null )
  15. navMgr = new NavigationManager()
  16.  
  17. return navMgr;
  18. }
  19.  
  20. public static void next()
  21. {
  22. getInstance.showNextPage();
  23. }
  24.  
  25. public static void prev()
  26. {
  27. getInstance.showPrevPage();
  28. }
  29.  
  30. public void showPrevPage()
  31. {
  32. currentNode--;
  33. showpage();
  34.  
  35. }
  36.  
  37. public void showNextPage()
  38. {
  39. if ( currentPage == null ) {
  40. BaseModel mdl = (BaseModel) navMdl.get( currentNode );
  41. String pagename = (String) mdl.get();
  42. currentPage = (BasePage) ProjectManager.getPageManager().getPage( pagename );
  43. }
  44. if ( currentPage.checkValidations() == 0 ) {
  45. currentNode++;
  46. showpage();
  47. }
  48. }
  49.  
  50. public void showHomePage()
  51. {
  52. currentPage = null;
  53. currentNode = 0;
  54. BaseModel mdl = (BaseModel) navMdl.get( currentNode );
  55. String pagename = (String) mdl.get();
  56. currentPage = (BasePage) ProjectManager.getPageManager().showPage( pagename );
  57. }
  58.  
  59. private void showpage()
  60. {
  61. BaseModel mdl = ( BaseModel ) navMdl.get( currentNode );
  62. String pagename = ( String ) mdl.get();
  63. currentPage = ( BasePage ) ProjectManager.getPageManager().showPage( pagename );
  64. }
  65.  
  66. }

This mechanism can be used in conjunction with Library Functions to manage the page navigation in a simple and easy to maintain way. Take the following XML page declaration for example.

A navigation page XML declaration

  1. <Page class="net.co.test.NavPanel" style="Heading">
  2. <Components>
  3. <Image name="nextButton" x="564" y="51" w="19" h="18" content="right.gif"/>
  4. <Image name="prevButton" x="60" y="54" w="19" h="18" content="left.gif"/>
  5. </Components>
  6. <Events>
  7. <Event method="net.co.test.NavigationManager.next" target="nextButton" type="MouseHandler"/>
  8. <Event method="net.co.test.NavigationManager.prev" target="prevButton" type="MouseHandler"/>
  9. </Events>
  10. </Page>
  11.  

This code will call the static methods of the NavigationManager class and the NavigationManager will take care of presenting the correct page. Of course this can be extended to take care of multiple routes through an application or to repeat certain pages which will bind to different parts of the model and it should be obvious that pages can be added or rearranged without any difficulty.

Mapping page names

A page name can now be paramaterized or mapped, so that a frames file or startup.properties file can specify a key instead of a page name. This key can then be mapped to a specific page depending on the context or on startup parameters.

Setup the application lifecycle listener to set the initial page name

  1. // In startup.properties specifc the Lifecycle object
  2. // LifeCycleListener=my.project.ProjectListener
  3.  
  4. // In that object map the "START_PAGE" page name
  5. public class ProjectListener implements LifeCycleListener
  6. {
  7. /**
  8.   * Called when the application/applet has been created and initialized.
  9.   * @param project the owner project
  10.   */
  11. public void initialize( Project p )
  12. {
  13. String[] args = (String[])project.getObject( "StartupArgs" );
  14. // Set the start page to the 3rd command-line parameter
  15. p.getPageManager().mapPageName( "START_PAGE", args[ 3 ] );
  16. }
  17. }

and instead of hardcoding a page name in the frames file it can be replaced with the appropriate key:

Set the content using a key instead of a page name

  1. // In the frames.xml include the "START_PAGE" key
  2. <Frame name="content" constraint="content" width="500" height="200"
  3. content="START_PAGE" title="Home" icon="ac0036-16.png" sidebar="west"/>

Application Styles

Aria supports multiple application styles, including Single Document Interface (SDI) and Multiple Document Interface (MDI) styles with and without docking of the internal windows.

Using the various application types you can create a variety of applications from the simple to the complex. Most applications will rely on an SDI style of interface for simplicity, but others such as management consoles and tool style applications can make good use of a docking framework to help manage the user interface complexities that arise from hierarchies and structures of the application domain.

Application styles are simple dictated by the startup applet used to launch an application and most of the remaining code and configuration is common to all applications types. In some cases, switching between application styles is also possible, provided that attention is paid to things like page navigation and framesets.

Application styles

Aria supports multiple application styles as metntioned above. These styles are:

Application styles

Style

Description

Basic

With the basic application style you get a single window in which single pages or simple framesets can be displayed. This type of setup corresponds toa Single Document Interface

Desktop

The desktop application style is equivalent to the Multiple Document Interface or MDI, where multiple windows can be used and each has a header, allowing the window to be moved around a desktop panel.

Docking

The docking application style is almost a cross between SDI and MDI applications, whereby multiple windows are allowed, but where those windows are managed either by arranging them in tabbed windows or by docking them into sidebars.

Generally it seems that users prefer SDI type applications , or some form of docking, however some application such as complex management applications may justify use of MDI. The choice of one application style depends on your particular application and changing from one to another is just a matter of choosing the appropriate startup class.

Docking Frames

Docking frames allow multiple windows, with headers, titles and icons to be displayed in various areas of the screen. Multiple windows or targets can be placed in each of these areas. The docked windows can be minimized to sidebars and later restored to their original location. The docked windows may also be dragged from one area to another thereby allowing users to customize the layout of their application.

Docking frames also show a preview of the docked window when the mouse is moved over the sidebar button. Using these `sliding' windows can save valuable screen real estate and help reduce clutter. A user might, for instance, minimize a window to the sidebar when the information contained in that window is only of occassional interest. Then, in order to consult the window all she need do is move the mouse over the sidebar button to view the window contents once more. A simple click will then dismiss the preview.

A docked panel may also be zoomed in on so that it occupies almost all the available screen area (apart from the toolbars, menus and sidebars). To zoom in on a panel just double click the header, then to restore the view just double click the header once more.

Using the docking layout

As mentioned above, to use the docking layout you just need to select the appropriate startup application org.formaria.swing.docking.DockingApp

Startup for the docking application style

  1. java -cp AriaRuntimeCommon.jar org.formaria.swing.docking.DockingApp

The FrameTest sample application can be seen below when used with the Docking application style. The application is shown with half of its windows minimized, two to the south or bottom docking sidebar and one to the east or right handside sidebar

Setting the layout configuration

The basic frameset is specified using the frames file that was used for the basic frames setup earlier (see Framesets.), but for docking layouts we must add some extra information detailing how the screen is to be divided up. For example the following:

Setting up a docking layout

  1. <FrameSet layout="(COLUMN (ROW weight=1.0 left
  2. (COLUMN middleTop content middleBottom) right) bottom)">
  3. <Frame name="left" constraint="left" content="p1"
  4. icon="aria_icon.png" sidebar="west" title="Dock to the left"/>
  5. <Frame name="right" constraint="right" content="p2" icon="aria_icon.png" sidebar="east"/>
  6. <Frame name="middleTop" constraint="middleTop" content="p3" icon="aria_icon.png" sidebar="west"/>
  7. <Frame name="middleBottom" constraint="middleBottom" content="p4" icon="aria_icon.png" sidebar="south"/>
  8. <Frame name="content" constraint="content" content="Welcome" icon="aria_icon.png" sidebar="west"/>
  9. <Frame name="bottom" constraint="bottom" content="p6" icon="aria_icon.png" sidebar="south"/>
  10. </FrameSet>

The docking layout is specified as an attribute of the FrameSet element, and follows the format specified by the SwingLabs MultiSplitLayout layout manager (see http://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html ).

Docking options

Each of the docked frames has a number of properties that yo can use to customize the appearance of the docked frame, and these are:

Docking frame attributes

Attribute

Usage

icon

Specifies the name of an icon to use in the frame's header.

sidebar

The name of the side bar into which this target docks when minimzed. The options are:

west - dock into the west or left hand side sidebar

south - dock into the south or bottom edge sidebar

east - dock into the east or right hand side sidebar

title

The title of the frame.

constraint

The name of the docking area into which this frame or panel normally docks. The constraint should match one of the areas specified by the FramsSet 's config attribute.

minimized

If true , sets the initial state of the panel to be minimized.

Embedded applications

Not all applications are standalone and in some cases an application needs to coexist with legacy functionality or functionality obtained from a third party. Therefore there are situations where an application does not have direct control of its startup or of its frame. For this purpose Aria and Aria applications can be embedded in a standard Swing application as though the Aria application was just a Panel or just another component being used by the application.

The key to embedding a Aria application is the XContentHolder interface which describes the content displayed to the user. The interface is used by the page manager to display the pages that makeup the Aria application.

Modular application

Just as a Aria application can be embedded in a legacy application, one Aria application can be embedded in another. In fact multiple Aria applications can coexist within the one JVM. Applications used in this way can also exist as standalone entities in their own right. Turning this about, applications can therefore be built in a modular way, such that a component or module can also exist and operate as a complete application.

Building applications in a modular way allows you to deliver functionality in a targeted way, depending on things like user roles/needs, security/access rights, connectivity levels, development schedules or even for commercial reasons (e.g. advanced features for power users). With a modular architecture you can be more targeted with your delivery of functionality.

Aria's support for modular applications extends throughout the platform and apart from the loading of the modules you can largely regard the separate modules as being part of the complete application from a development standpoint.

Set startup objects

Multple startup objects can be added simply by adding startup property entries in the form:

Listing the startup objects

  1. StartupObject<N>=Name;className
  2. StartupObject<N>=Name;className;Project

where is a counter starting from 0. In the second example the object is constructed via a constructor that takes an instance of the project as an argument

While this may seem like an insignificant change it opens up the possiblity of having multiple applications running within the same JVM and it means that an application can be built from modular elements, elements that can exist as standalone applications or as integrated modules.

Relaunching applications

Support for the system tray or launch area has been added. When a system tray icon is added the application can choose to stay resident in memory after the main window has been closed by setting the " ExitOnClose " startup property to false. The advantage of doing this is that the data model does not need to be reinitialized if the main window is just being redisplayed. This initialization can be significantly quicker than normal startup as all the classes needed by the JVM are already loaded. If remote data is used the startup gain may be even greater as network access may not be required.

The tray icon provides two menu items, one to open the window and the second to close the window and exit the JVM.

This version of the class uses the SwingLabs version of the tray support and is therefore limited to Swing. If JDK 6 or later is being used then the JVM's tray support could be used for non Swing applications.

The system tray/stay resident option may be particularly valuable when used in conjunction with an embedded webserver, as a remote server could thereby signal the application to wake up and display new data or alerts as when such data becomes available by simply making a request to the client application's server. The same process could also be used to provide a tighter integration between a web page/web application and a Aria application.

To use this support the application must add an instance of the org.formaria.swing.deploy.SystemTrayManager class. A good place to do this is in a LifeCycleListener implementation

Setting up the system tray

  1. public class ProjectListener implements LifeCycleListener
  2. {
  3. private static SystemTrayManager sysTray;
  4. /**
  5.   * Called when the application/applet has been created and initialized.
  6.   * @param project the owner project
  7.   */
  8. public void initialize( Project proj )
  9. {
  10. final Project project = proj;
  11. if ( sysTray == null ) {
  12. sysTray = SystemTrayManager.getInstance( project );
  13.  
  14. // Do remaining initialization and network access
  15. }
  16. }
  17.  
  18. /**
  19.   * Called when the application/applet has been shutdown and is about to exit
  20.   */
  21. public void shutdown()
  22. {
  23. }
  24. }

Embedded webserver

A minimal http server is now embedded at org.formaria.http.HttpServer . When customized this server will allow a server appplication to signal a client via a http request. By customizing the server with a response handler the application can then respond with special actions, such as a wake-up or by requesting updates/new data from the server.

JNLP support

As an alternative to using the embedded webserver an application can use the JNLP API to request that it is a singleton, that is that only one instance of the application should run at a time. The SystemTrayManager takes care of most of the work and it will invoked the Activation interface specified by a project if one is present if an attempt is made to relaunch the JWS application while the instance is still in memory . The activation object is specified as follows sometime following startup:

Setting the activation object

  1. project.setObject( "ActivationObject", this );

Typically the application will restart its user interface once reactivated. Of course care should be taken to do this on the EventDispatchThread.

A typical restart handler2

  1. public void activate()
  2. {
  3. String[] args = (String[])project.getObject( "StartupArgs" );
  4. final String eventType = args.length > ProjectListener.EVENT_TYPE_ARG ?
  5. args[ ProjectListener.EVENT_TYPE_ARG ] : null;
  6. final String eventID = args.length > ProjectListener.EVENT_TYPE_ID ?
  7. args[ ProjectListener.EVENT_TYPE_ID ] : null;
  8.  
  9. // Query the startup event in the background
  10. if (( eventID != null ) &amp;&amp; ( eventID.length() > 0 )) {
  11. SwingWorker worker = new SwingWorker()
  12. {
  13. public Object construct()
  14. {
  15. // Do the work off the EDT
  16. String url = "workspaces/myevent/" + eventID;
  17.  
  18. ProjectListener.queryEvents( project, eventType, url );
  19. return null;
  20. }
  21.  
  22. public void finished()
  23. {
  24. // Now, on the EDT update the tree and show the event.
  25. getBinding( taskList ).get();
  26. showEvent( eventID, eventType );
  27. setSelectedNode( eventID );
  28. }
  29. };
  30. worker.start();
  31. }
  32. }
  33.  

To use this type of functionality the application must make sure that the application does not exit the JVM when the main frame is closed, and this is done with by overloading the XLifeCycleListener's shutdown method - the tray manager takes care of the rest

Prevent JVM Exit

  1. /**
  2.   * Initialize the project's connection
  3.   */
  4. public class ProjectListener implements LifeCycleListener
  5. {
  6. ...
  7.  
  8. /**
  9.   * Called when the application/applet has been shutdown and is about to exit
  10.   */
  11. public void shutdown()
  12. {
  13. }

Input Validation

Aria input validation allows you to define your validations in a single file so that the validation rules contained in the file can be used application wide or even across applications. Built-in validations provide common validations such as `mandatory' and `min-max' validations. The validation rules provide predictable and consistent behavior making your code much easier to maintain and extend than the alternative of checking individual inputs on an ad hoc basis.

The validation mechanism can also make it easier to localize an application or customize things like input limits for different environments or contexts.

In this section you will see how to implement the simple validations and how to handle validation errors and exceptions. The chapter will also show how to build customized validations.

Adding Basic Validations

The first step in adding input validations is to define our validation rules in an XML file and amend the startup.properties file to refer to the file.

Create a new file and name it ` validations.xml '. In the file create a simple validation file in the resources directory of your project.

A simple validation file

  1. <Validations>
  2. <validation name="firstname" type="mandatory" msg="Firstname cannot be blank"/>
  3. </Validations>

The name attribute is what we refer to when referencing the validation from a page. The type attribute defines the validation type and the msg attribute is the message which is shown should the validation fail.

The startup.properties file needs to refer to the new validations file. Setting the validations file can be carried out through the project properties page within the editor or manually by adding the following line to the startup.properties file. If no `Validations' property is specified the application will default to `validations.xml' and use it if found.

New line in the startup.properties

  1. Validations=validations.xml

Now a page can be created with an edit field which needs to be validated. To provide data for the field and to map the input field to the data model a binding can be added within the data section. A button has also been added which will be used to trigger the validation.

The validated page

  1. <Page class="org.formaria.test.Personal" style="prompt">
  2. <Components>
  3. <Label x="144" y="110" w="100" h="20" style="prompt" content="Firstname:"
  4. alignment="Left" opaque="false"/>
  5. <Edit name="firstnameText" x="294" y="108" w="178" h="20" style="data" alignment="Leading"/>
  6. <Button name="validateBtn" x="220" y="250" w="200" h="30" content="Validate"/>
  7. </Components>
  8. <Events>
  9. <Event method="doValidation" target="validateBtn" type="ActionHandler"/>
  10. </Events>
  11. <Validations>
  12. <Validation rule="firstname" target="firstnameText"/>
  13. </Validations>
  14. <Data>
  15. <Bind target="firstnameText" source="customer/firstname" output="customer/firstname"/>
  16. </Data>
  17. </Page>

Once the validation rule has been setup and bound to a component, the validation needs to be triggered and validation error messages also need to be presented to the user.

From the page's XML file it can be seen that the class which implements the event handlers is ` org.formaria.test.Personal '. For now, simply insert the following function into the class:

Prepare to handle validations with an event handler function

  1. public void doValidation()
  2. {
  3. int ret = checkValidations();
  4. }

If the application is run and the button clicked, the Java console will show the exceptions being thrown with the text of the validation. If some text is entered into the textbox and the button clicked again no exception is now thrown indicating that everything is OK.

The checkValidations function returns a value indicating the status. A decision can be made what to do depending on the value returned from the function. The return values are defined as constants in the org.formaria.aria.validation.Validator interface.

This code works but it is not a particularly elegant way of handling the validations and the section Writing a custom exception handler., will also look at how to handle and display the validations in a more programmer friendly way.

Adding component validations

The validation described above was triggered by a programmed event and the same validation would also be triggered upon a page transition. Component validations on the other hand are triggered when the user interface component being validated has lost focus. Start by opening the validations.xml file and adding a new minmax validation. The new validation appears as below

An in-line validation

  1. <validation name="age" type="minmax" min="18" max="65"
  2. msg="Customer age must be between {min} and {max}" mandatory="false"/>

In this case the type is ` minmax ' indicating that this validation is to be used with numeric data. The ` min ' and ` max ' attributes contain the limits of the input. The message contains the two pieces of text ` {min} ' and ` {max} '. These values will be substituted with the values contained in the ` min ' and ` max ' attributes whenever the validation is triggered. The ` mandatory' attribute indicates whether or not the field must contain data when the page level validations are being carried out.

Next add another edit component to the ` personal ' page and add the validation below.

  1. <Validation rule="age" target="ageText"/>

Start the application once more and enter a value outside the specified range into the new edit field. Hit the tab key and you will notice an exception being output in the Java console. Do the same with a value within the range and no exception is output.

To highlight the behavior of the validation a few input combinations are listed. For clarity enter some data into the firstname edit so that it does not trigger the mandatory validation.

  • Clear the data in the age edit field and click the button. No exception is output as the mandatory attribute is set to false .
  • Enter some invalid data into the edit field, hit the tab field to generate the exception and click the button. In this case the page level validation triggers an exception as the text entered into the field is outside the permissible range even though the mandatory flag is false .
  • Change the mandatory flag to true and restart the application. Leave the age field blank and click the button. In this case an exception is triggered as the field cannot be blank.

Validation attribute evaluation

Aria 3.0 extends the use of evaluated attributes to most areas of the framework, including validations. Evaluated attributes allow you to change the behavior of a validation at runtime by way of callbacks to your code or to library code.

Handling exceptions

The attribute evaluator contains an exception handler that gets called in case of an exception and can override the result returned by the attribute evaluator. The evaluator may be of use in case, for example, an evaluation depends on a list selection and where that list may not have a selected values - the list would otherwise return a value such as null or -1 to indicate the error and this is probably not a valid value for the evaluated attribute. Say a path of a/b/${c}/d/e is enetered and ${c} depends on say a list selection and that list is not fully initialized.

Validation lifecycles

Validation can be invoked at various times within an application. For the most part the lifecylce of an individual validation instance is tied to the lifecycle of the component and page to which it is bound.

At the finest level a validation may be triggered by a change in a components data, or by intervention by the programmer, while at a courser level the validations can be triggered prior to page transition.

Validation on Page Transition

Normally validations are checked when a page transition occurs if the TriggerValidations startup property is set, however on versions of Aria prior to 3.2 this value was not added to the startup file by the New Aria Project wizard , so it wass necessary to set the value manually.

Once the TriggerValidations flag is set a validation error will prevent the page transition from occuring.

Writing a custom exception handler

The method of reporting the validations up to now is crude and does not give you much control of error reporting. In order to manage the validations a custom exception handler can be written.

Writing a custom exception handler is straightforward and allows you the same code to be used for error handling on all of pages in an application. A new class named ` ExceptionHandler ` which provides an implementation of the ExceptionHandler interface is created.

The ExceptionHandler interface contains two methods.

The handleException method definition

  1. public boolean handleException( Object c, Exception ex, Object checker );

The handleException method takes three parameters and returns a boolean value. The first parameter is the component being validated, the second the exception, and the third is the validator which is carrying out the validation. The method should return true if further validations are to be carried out and false if no more validations are required. This chaining of validations allows you to control how the validations are used and how error messages are displayed. Stopping a chain of validations allows you to prevent the user being bombarded with error messages if, for instance, a single input field triggers multiple validation errors.

The accumulateMessages method definition

  1. public int accumulateMessages( boolean accumulate, int level );

Another method is now required, the accumulateMessages method is called twice when the Page.checkValidation s method is invoked. The first time is before any validations have been done so as to inform the ExceptionHandler that page level validations have begun. In this case the first parameter is true . Then, it is called once more when all of the validations have completed and in this case the first parameter is false .

The second parameter indicates the most serious level of error encountered so far. the value of this parameter can be overridden within the implementation of the accumulateMessages method by returning a different value from the method. Otherwise it is expected that the second parameter is returned.

Now we will implement our custom ExceptionHandler class as shown below. The class is shown in its entirety as it is to explain it section by section when the context of each section can be seen.

The entire ExceptionHandler class

  1. package org.formaria.test;
  2.  
  3. import java.util.*;
  4. import java.awt.*;
  5. import org.formaria.aria.*;
  6. import org.formaria.aria.exception.*;
  7. import org.formaria.aria.validation.*;
  8.  
  9. public class ExceptionHandler implements ExceptionHandler
  10. {
  11. boolean pageValidation = false;
  12. private Page currentPage;
  13. Vector errors, warnings;
  14.  
  15. public ExceptionHandler( Page page )
  16. {
  17. currentPage = page;
  18. }
  19.  
  20. public boolean handleException( Object comp, Exception ex, Object xvalidator )
  21. {
  22. Validator validator = ( Validator )validator;
  23. if ( ( validator.getLevel() == validator.LEVEL_ERROR ) && ( ! pageValidation ) ){
  24. currentPage.showMessage( "Input error", ex.getMessage() );
  25. return true;
  26. }
  27. String msg = validator.getMessage();
  28. if ( validator.getLevel() == validator.LEVEL_ERROR )
  29. errors.add( msg );
  30. else
  31. warnings.add( msg );
  32.  
  33. return true;
  34. }
  35.  
  36. public int accumulateMessages( boolean start, int level )
  37. {
  38. pageValidation = start;
  39. if ( pageValidation ){
  40. errors = new Vector();
  41. warnings = new Vector();
  42. } else {
  43. if ( errors.size() > 0 || warnings.size() > 0 ) {
  44. Toolkit.getDefaultToolkit().beep();
  45. currentPage.showMessage( "Error", formatMessages() );
  46. }
  47. }
  48. return level;
  49. }
  50.  
  51. private String formatMessages()
  52. {
  53. String msg = "";
  54. for ( int i = 0; i < errors.size(); i++ )
  55. msg += "error :" + errors.elementAt( i ) + "\n";
  56. for ( int i = 0; i < warnings.size(); i++ )
  57. msg += "warning :" + warnings.elementAt( i ) + "\n";
  58. return msg;
  59. }
  60. }

The constructor takes the page being validated as a parameter. This is stored in a member variable for use when the validations are being done.

The ExceptionHandler constructor

  1. public ExceptionHandler( Page page )
  2. {
  3. currentPage = page;
  4. }

The handleException method handles component and page level validations. In the case of component validations the level will be XValidator.LEVEL_ERROR passed exception is simply displayed.

In the case where the pageValidation flag is true the message is added to the appropriate storage. The idea behind this is that the reporting of the errors can be controlled instead of having to display individual messages (and require the user to dismiss multiple error messages) or only most severe errors need be displayed.

Implementing the handleException method

  1. public boolean handleException( Object comp, Exception ex, Object xvalidator )
  2. {
  3. Validator validator = ( Validator )validator;
  4. if ( ( validator.getLevel() == validator.LEVEL_ERROR ) && ( ! pageValidation ) ){
  5. currentPage.showMessage( "Input error", ex.getMessage() );
  6. return true;
  7. }
  8. String msg = validator.getMessage();
  9. if ( validator.getLevel() == validator.LEVEL_ERROR )
  10. errors.add( msg );
  11. else
  12. warnings.add( msg );
  13.  
  14. return true;
  15. }

The accumulateMessages method is shown below

Implementing the accumulateMessages method

  1. public int accumulateMessages( boolean start, int level )
  2. {
  3. pageValidation = start;
  4. if ( pageValidation ){
  5. errors = new Vector();
  6. warnings = new Vector();
  7. } else {
  8. if ( errors.size() > 0 || warnings.size() > 0 ) {
  9. Toolkit.getDefaultToolkit().beep();
  10. currentPage.showMessage( "Error", formatMessages() );
  11. }
  12. }
  13. return level;
  14. }

The first line sets the member variable ` pageValidation ' to the value passed in the ` start ' parameter.

If start is true the storage vectors are initialized; one to store warnings and the other to store errors.

Alternatively, if start is false something needs to be done with the errors and warnings that have been accumulated. In this case, the showMessage method of the currentPage is called which will display the errors and warnings in a simple text dialog with an OK button. A decision on what to do next can be made in the ` personal ' page where the checkValidations method was called depending on the values returned from the accumulateMessages method.

As mentioned above this error handling mechanism provides control over the error reporting mechanism and another way to handle the validations in the case where there are only warnings is to display a dialog listing the warnings and asking `Do you wish to continue?' along with ` Yes ' and ` No ' buttons. If the Yes button is clicked then the value XValidator.LEVEL_IGNORE can be returned which will give the page the impression that no validations were generated.

Writing a custom validation factory and custom validations

The basic ValidationFactory class can construct the validation types discussed so far but custom validations might be required, and this is done by means of writing a custom validation factory.

Start by creating the class AValidationFactory which extends the ValidationFactory class.

The custom AValidationFactory

  1. package org.formaria.test;
  2.  
  3. import java.awt.*;
  4.  
  5. import org.formaria.debug.*;
  6. import org.formaria.xml.*;
  7. import org.formaria.aria.build.*;
  8. import org.formaria.aria.validation.*;
  9.  
  10. public class AValidationFactory extends ValidationFactory {
  11.  
  12. public Validator getValidation( String validationName, Method m, int mask, Object page )
  13. {
  14. return super.getValidation( validationName, mask, page );
  15. }
  16.  
  17. }

For now the new class simply implements the getValidation method and returns the call to the super method.

In order to start using this custom class the following line needs to be added to the startup.properties file

The startup.properties line

  1. ValidationFactory=org.formaria.test.ValidationFactory

If the application is run now, it will work exactly as before except that our new validation factory is now controlling the construction of the validation rules.

Now a custom validation class can be created. As an example a simple number validation class can be created which will make sure that entered data is numeric. Create a class called NumberValidation which extends the BaseValidator class. Create the validate method which carries out the validation. In the NumberValidator class the Double.parseDouble method is used to parse the text contained in the component. If an exception occurs it can be assumed that the validation has failed, the errorLevel is set and an exception is thrown.

The NumberValidation class

  1. package org.formaria.test;
  2.  
  3. import java.awt.*;
  4.  
  5. import org.formaria.aria.validation.*;
  6.  
  7. public class NumberValidation extends BaseValidator {
  8.  
  9. public void validate(Object c, boolean forceMandatory) throws Exception
  10. {
  11. String text = getText(c);
  12. if (text.trim().length() > 0) {
  13. try {
  14. Double.parseDouble(text);
  15. }
  16. catch (Exception ex) {
  17. errorLevel = LEVEL_ERROR;
  18. throwException();
  19. }
  20. }
  21. }

Now the custom validation factory class needs to be amended to create and return the NumberValidator class whenever the text ` number ' is passed in the validationName parameter of the getValidation function.

The modified getValidation method

  1. public Validator getValidation( String validationName, Method m, int mask, Object page )
  2. {
  3. XmlElement ele = ( XmlElement ) validations.get( validationName );
  4. String type = ele.getAttribute( "type" );
  5.  
  6. if ( type.compareTo( "number" ) == 0 ) {
  7. NumberValidation validator = new NumberValidation();
  8. validator.setName( validationName );
  9. validator.setMask( mask );
  10. validator.setup( ele );
  11. return validator;
  12. } else {
  13. return super.getValidation( validationName, mask, page );
  14. }
  15. }

The line ` validator.setup(ele) ' deserves some explanation as this allows whatever arbitrary attributes might be required for the custom validator to be set. The ele variable is the entire validation node as defined in the validations.xml file and can be interrogated by the custom validator for whatever tags are used.

Now open the validations file and enter a new validation of type ` number '

Declaring a new number validation

  1. <validation name="price" type="number" msg="The price must be a number"/>

Next, open the personal.xml file and enter a definition for a new edit component and name it priceText . Now enter a new validation for the component as follows

  1. <Validation rule="price" target="priceText"/>

If the application is run now it can be seen that the new field has page level as well as component validations. You can remove the component validation by changing the validate function as follows.

  1. public void validate(Object c, boolean forceMandatory) throws Exception {
  2. if (forceMandatory) {
  3. String text = getText(c);
  4. if (text.trim().length() > 0) {
  5. try {
  6. Double.parseDouble(text);
  7. }
  8. catch (Exception ex) {
  9. errorLevel = LEVEL_ERROR;
  10. throwException();
  11. }
  12. }
  13. }
  14. }

The forceMandatory flag is set to true when the validation is page level so by doing nothing when it is false removes the component validation.

Finally the new validation class is changed to handle the mandatory attribute. This is achieved by overloading the setup method of the BaseValidator class as mentioned earlier.

The overloaded setup class

  1. public void setup( XmlElement ruleConfig, XmlElement instanceConfig )
  2. {
  3. String value = ruleConfig.getAttribute( "mandatory" );
  4. mandatory = value.compareTo( "true" ) == 0 ? true : false;
  5. super.setup( ruleConfig, instanceConfig );
  6. }

This is wherethe attributes defined in the validations.xml file can be used. Set the mandatory attribute to true and run the application again.

Handling the mandatory flag

  1. public void validate(Component c, boolean forceMandatory) throws Exception {
  2. if (forceMandatory) {
  3. String text = getText(c);
  4. if (text.trim().length() > 0) {
  5. try {
  6. Double.parseDouble(text);
  7. }
  8. catch (Exception ex) {
  9. errorLevel = LEVEL_ERROR;
  10. throwException();
  11. }
  12. } else if ( mandatory ){
  13. errorLevel = LEVEL_ERROR;
  14. throwException();
  15. }
  16. }
  17. }

Simply throw the exception if the mandatory flag is true and the text is zero length.

Registering Validations

Aria 3.0 introduced a new mechanism for registering validations and this mechanism falls into line with the other registrations used by Aria. Instead of using a validation factory as described above a validation can be added by adding a validations xml file. By default the project will look for a validations.xml file in a project (or a file named by the Validations startup property. An example of such a file is shown below:

A sample validations file

  1. <Validations>
  2. <validation name="firstname" type="mandatory" msg="VALID_FNAME" first="true"/>
  3. <validation name="surname" type="mandatory" msg="VALID_SNAME"/>
  4. <validation name="dob" type="mandatory" min="sixtyYearsAgo()" max="eighteenYearsAgo()"
  5. msg="Date of birth must be a valid date, and the applicant must be between the ages of 18 and 60"/>
  6. <validation name="propvalue" class="org.formaria.mortgage.MinMaxValidator" min="2000"
  7. max="2000000" msg="VALID_PROPVALUE" mandatory="true"/>
  8. <validation name="mortamt" class="org.formaria.mortgage.MinMaxValidator" min="2000"
  9. max="1000000" msg="VALID_MORT_AMT" mandatory="true"/>
  10. <validation name="createApp" type="function"
  11. msg="Please select 'Sole' or 'Joint' in order to proceed!"/>
  12. <validation name="openApp" type="function" msg="Please select a file to proceed!"/>
  13. <validation name="filename" type="mandatory" msg="Please enter a filename!"/>
  14. <validation name="mortratio" type="function" msg="Mortgage amount is greater than property value!"/>
  15. </Validations>

This is now the preferred mechanism for adding validations, but the old method should still work.

Getting values from the Page

There are a number of ways of getting validation information from the page being validated which can be very useful

First open the personal.xml file which has been used up to now and change the minmax validation using the `age' validation to the following

Modifiied age validation

  1. <Validation rule="age" target="ageText" method="getAge"/>

The new ` method ' attribute declares the name of the method within the page which is to be used to retrieve the value being validated. Now the getAge method is created in the page.

Create the getAge method

  1. public String getAge()
  2. {
  3. System.out.println( "In the getAge() function" );
  4. Edit ageText = ( Edit )findComponent( "ageText" );
  5. return ageText.getText();
  6. }

If the application is run now the first line is output to the console when the age is being validated. Now, this does exactly the same as previously but this way of getting values can be useful where more behind the scenes work needs to be done in order to carry out the validation.

This leads to the next type of validation for which another custom validation can be written. The validation is a FunctionValidation which will simply call a function within the page. The specified function will return the error level. Start by creating the following class which extends the BaseValidator class

The new FunctionValidation

  1. package org.formaria.test;
  2.  
  3. import java.awt.*;
  4.  
  5. import org.formaria.xml.*;
  6. import org.formaria.aria.validation.*;
  7.  
  8. public class FunctionValidation extends BaseValidator {
  9.  
  10. public void validate(Object c, boolean forceMandatory) throws Exception {
  11. Integer ret = ( Integer ) invokeMethod();
  12. if ( ret.intValue() > LEVEL_IGNORE ) {
  13. errorLevel = ret.intValue();
  14. throwException();
  15. }
  16. }
  17.  
  18. public void setup( XmlElement element )
  19. {
  20. message = element.getAttribute( "msg" );
  21. super.setup( element );
  22. }
  23.  
  24. }

Within the setup method the validator setting the message which needs to be used with this validator.

The validate function invokes the registered method. It expects a return type of Integer which it can then throw an exception for if the error level is greater than LEVEL_IGNORE .

Next define a validation for this type in the validation.xml file

The agefunction validation declared

  1. <validation name="agefunction" type="function" msg="The agefunction validation triggered this"/>

Now the custom validation factory needs to be amended to construct the new validation. The getValidation function should now look like this

The modified getValidation function

  1. public Validator getValidation( String validationName, Method m, int mask, Object page )
  2. {
  3. XmlElement ele = ( XmlElement ) validations.get( validationName );
  4. String type = ele.getAttribute( "type" );
  5.  
  6. if ( type.compareTo( "number" ) == 0 ) {
  7. NumberValidation validator = new NumberValidation();
  8. validator.setName( validationName );
  9. validator.setMask( mask );
  10. validator.setup( ele );
  11. return validator;
  12. } else if ( type.compareTo( "function" ) == 0 ) {
  13. FunctionValidation validator = new FunctionValidation();
  14. validator.setName( validationName );
  15. validator.setup( ele );
  16. return validator;
  17. } else {
  18. return super.getValidation( validationName, mask, page );
  19. }
  20. }
  21.  

Reopen the personal.xml file and insert the following validation.

Implementation of the function validation

  1. <Validation rule="agefunction" target="ageText" method="doAgeValidation"/>

The method being called in this case is the `doAgeValidation' method so that function needs to be defined within the page.

The doAgeValidation function

  1. public Integer doAgeValidation()
  2. {
  3. return new Integer( Validator.LEVEL_ERROR );
  4. }

As mentioned earlier the method needs to return an Integer object which contains the error level. The reason for using this type of validation is that any type of complex checking can be carried out and the appropriate level can be returned. These validations can be quite complex and would be very difficult to describe in XML. If fact an XML syntax would probably have to be developed in order to describe this type of validation so it's just easier to have Java do the work for us.

From the examples here it might seem that quite a lot of work needs to be done in order to setup custom validations but it is done in this way so that a more maintainable codebase and consistent way of handling validations can be achieved.

Adding validation feedback

Limited visual feedback can be provided by the validations as they are evaluated, so that special colors are used for failed validations. Adding such feedback makes it easy for the user to locate the inputs that block progress within an application due to failed validations.

The normal, warning and failure colors used for these validations can be set using the BaseValidator.setValidationColors , however the validations can also be set using an extended style reference in the validations file

Add a style parameter to the validations file

  1. <Validations style="validationStyle">
  2. <validation name="firstname" type="mandatory" msg="Firstname cannot be blank"/>
  3. </Validations>

The style attributes are then specified in the styles file as normal:

Add the styles to the styles file

  1. <style name="validationStyle">
  2. <colorNormal value="ffffff"/>
  3. <colorWarn value="ffffd5"/>
  4. <colorFail value="ffe6d5"/>
  5. </style>

Menus and Toolbars

Some applications require menus for common operations like File, Save, Print and so on. Although rarely used, Aria provides a means to include a menu bar and a simple mechanism for defining the content of that menu.

Creating a Menu

In the XML page descriptions menus can be added by inserting an MenuBar element. The menu bar element can contain Menu elements which in turn can contain MenuItems . For the Menu and MenuItem elements the content attribute is used as the menu text. A MenuItem without a content attribute will be added as a menu separator. The content of the menu can be the key into the language resource bundle for the current application language.

Creating a simple menu

  1. <?xml version="1.0" encoding="ISO-8859-1"?>
  2. <Page>
  3. <MenuBar>
  4. <Menu content="File">
  5. <MenuItem name="newMenu" content="New..."/>
  6. <MenuItem/>
  7. <MenuItem name="saveMenu" content="Save"/>
  8. </Menu>
  9. </MenuBar>
  10. </Page>

An important consideration when creating menus is the lifecycle of a menu. While created as part of a page a menu does not come and go with the defining page but instead lives and is displayed for the duration of the application. Only one instance of the menubar is ever created, although items may be added to the menu at any stage.

A menu is an application wide component and is defined once for the application. One useful way of reinforcing this is to include the menu in the start page rather than inserting the menu definition directly as above.

Looking at amore complicated scenario a menu can include sub menus and can be composed of menus contributed by multiple pages (changing with the page context), however the editor does not yet support sub menu creation. A menu with sub menus is described below

A menu with sub menu items

  1. <Page layout="Border">
  2. <MenuBar>
  3.  
  4. <Menu content="File">
  5. <MenuItem content="New Country..." image="menu/new.png"/>
  6. <MenuItem/>
  7. <MenuItem content="Open Country..." image="menu/open.png"/>
  8. <MenuItem/>
  9. <MenuItem content="Save Coutry" image="menu/save.png"/>
  10. <MenuItem content="Save Coutry As..." image="menu/saveas.png"/>
  11. <MenuItem/>
  12. <MenuItem name="exitMI" content="Exit" image="menu/exit.png"/>
  13. </Menu>
  14.  
  15. <Menu content="Country">
  16. <MenuItem name="belgiumMI" content="Belgium" image="menu/belgium.png"/>
  17. <MenuItem name="englandMI" content="England" image="menu/england.png"/>
  18. <MenuItem name="franceMI" content="France" image="menu/france.png"/>
  19. <MenuItem name="germanyMI" content="Germany" image="menu/germany.png"/>
  20. <MenuItem name="irelandMI" content="Ireland" image="menu/ireland.png"/>
  21. <MenuItem name="italyMI" content="Italy" image="menu/italy.png"/>
  22. <MenuItem name="netherlandMI" content="Netherland" image="menu/netherland.png"/>
  23. <MenuItem name="portugalMI" content="Portugal" image="menu/portugal.png"/>
  24. <MenuItem name="scotlandMI" content="Scotland" image="menu/scotland.png"/>
  25. <MenuItem name="spainMI" content="Spain" image="menu/spain.png"/>
  26. <MenuItem name="switzerlandMI" content="Switzerland" image="menu/switzerland.png"/>
  27. </Menu> <
  28.  
  29. <Menu content="About">
  30. <MenuItem name="applicationMI" content="The Application" image="menu/application.png"/>
  31. <MenuItem name="authorMI" content="The Author" image="menu/author.png"/>
  32. <Menu content="Help">
  33. <MenuItem name="contentsMI" content="Contents" />
  34. <MenuItem name="faqMI" content="FAQ"/>
  35. </Menu>
  36. </Menu>
  37. </MenuBar>
  38. ....

Hooking up Events

Menu events can be hooked up to response handlers using the same event handling mechanism as used by any other component.

Menu Event Handling

  1. <Event type="MenuHandler" target="saveMenu" method="doSave"/>
  2. <Event type="MenuHandler" target="newMenu" method="doNew"/>

When considering the menu lifecycle and the longevity of the menu for conceptual reasons it is worth considering using a menus in conjunction with a frameset so that the menu handlers can be defined as part of a long lived page. Alternatively the menu definition can be declared in a separate XML file and included in another page using the include statement.

Because menus are not owned by a particular page in the application they cannot be looked up in the same way as other components which use the findComponent() method of the Page class. Instead the root menu can be obtained from the Applet for the current project.

Retrieving the application menu

  1. ProjectManager.getCurrentProject().getApplet().getMenuBar();

If a reference to a named menu item is required, then it's necessary to iterate the menus and find the child whose name corresponds. In a localized application, although the menus will be created automatically with the correct language, if the language is changed and the page manager reset, the menus will need to be reset manually. This is illustrated below.

Localizing a menu

  1. public class SomePage extends Page {
  2.  
  3. String[] name = { "mnuFile", "mnuFileNew", "mnuFileSave" };
  4. String[] tran = { "MNU_FILE", "MNU_NEW_PROJ", "MNU_FILE_SAVE" };
  5.  
  6. public void changeLanguage()
  7. {
  8. JMenuBar mBar = project.getApplet().getMenuBar();
  9. for ( int i = 0; i < mBar.getMenuCount(); i++ ) {
  10. iterateMenus( mBar.getMenu( i ) );
  11. }
  12. }
  13.  
  14. private void iterateMenus( JMenu menu )
  15. {
  16. setMenuText( menu );
  17. for ( int i = 0; i < menu.getMenuComponentCount(); i++ ) {
  18. setMenuText( menu.getMenuComponent( i ) );
  19. }
  20. }
  21.  
  22. private void setMenuText( Component c )
  23. {
  24. if ( c instanceof XMenu ) {
  25. ( ( XMenu ) c ).setText( getText( ( XMenu ) c ) );
  26. }
  27. }
  28.  
  29. private String getText( XMenu menu )
  30. {
  31. String name = menu.getName();
  32. for ( int i = 0; i < names.length; i++ ) {
  33. if ( name.compareTo( names[ i ] ) == 0 ) {
  34. return translate( translations [ i ] );
  35. }
  36. }
  37. return menu.getText();
  38. }
  39.  
  40. }

Editing menus

In the page editor you can load pages with menus and preview those menus by clicking the Show Menu button on the designers toolbar. The preview relies on there being a component or page selected as the menu is specific to the current page.

You can switch back and forth to the XML and edit the menus but the editor support is incomplete (as of version 2.0.6) as it does not provide an interactive way of setting the menu details such as the event response methods or menu texts.

Using multiple menus

[since Aria 2.0.7]

Since menus are defined on a page by page basis it is possible to define menus on more than one page, and since the menus are declared as children of a menu bar object it is worth note how this behaves in practice.

When a page is parsed the menu it declares is instantiated along with a menu bar, this new menu bar is given the name of the page within which it was declared. Once a menu bar has been created an attempt is made to set it as the applications menu bar. On the first occassion that this happens the new menu bar becomes the application's menu bar. Subsequently the menus belonging to the new menu bar are attached to the existing menu, thereby appending the new menus to the existing application menu bar.

If a page is reloaded (which might perhaps occur during the localization process) the menus will again be created using this process with the exception that the first page with a menu bar is treated specially and causes the existing menubar to be replaced.

Creating context sensitive menus

Some applications may require some menus or individual menu items to be context sensitive and why there is no mechanism to setup up such context sensitivity declaratively there are a number of options available.

Firstly, within the application's Java code it is possible to interact with the menus in the normal way. The menu bar can be retrieved from the application via the normal static methods (depending upon the widget set employed, in Swing for example you would call org.formaria.Applet.getMenuBar() ).

Secondly the menuHandler could be defined as an evaluated attribute, referring to a method in an instance of an javax.swing.Action object. The Action interface can then be used to set the enabled and disabled state of the menus. Check out the goodies section of the Xoetroep website for some more details.

Toolbars

Toolbars can be added to an application as part of the frames setup. A toolbar is considered to be a special type of page, loaded as part of the frameset.

The frameset can contain any number of target areas plus a toolbar element:

Setting up a toolbar in the frames.xml file

  1. <FrameSet
  2. config="(COLUMN (ROW weight=1.0 left (COLUMN middleTop content middleBottom) right) bottom)">
  3. <Toolbar name="toolbar" constraint="NORTH" content="toolbar"/>
  4. <Frame name="left" constraint="left" content="left" icon="wi0062-16.png" sidebar="west"
  5. title="A car to stir the emotions"/>
  6. <Frame name="right" constraint="right" content="right" title="Specifications" icon="wi0126-16.png"
  7. sidebar="east"/>
  8. <Frame name="middleTop" constraint="middleTop" content="middleTop" title="Citroen C6"
  9. icon="wi0124-16.png" sidebar="west"/>
  10. <Frame name="middleBottom" constraint="middleBottom" content="middleBottom"
  11. title="Engines" icon="wi0122-16.png" sidebar="south"/>
  12. <Frame name="content" constraint="content" content="Welcome" title="Gallery"
  13. icon="ac0036-16.png" sidebar="west"/>
  14. <Frame name="bottom" constraint="bottom" content="bottom" title="Dealers"
  15. icon="wi0063-16.png" sidebar="south"/>
  16. </FrameSet>

The toolbar itself is specified as any other page in your application. For the example shown above the toolbar.xml file contains the following:

toolbar.xml

  1. <Page layout="Flow" class="org.formaria.swing.toolbar.Toolbar" style="toolbar">
  2. <Guides width="640" height="480" vert="" horz=""/>
  3. <Layout alignment="left"/></P>
  4. <Components>
  5. <ToolbarButton name="FileBtn" content="File" style="toolbar" icon="wi0063-16.png"/>
  6. <ToolbarButton name="CutBtn" style="toolbar" icon="wi0122-24.png"/>
  7. <ToolbarButton name="CopyBtn" style="toolbar" icon="wi0064-24.png"/>
  8. <ToolbarButton name="PasteBtn" style="toolbar" icon="wi0009-24.png"/></P>
  9. </Components>
  10. </Page>

Dialogs

Dialogs are frequently used to display special information, information that is not presented during the normal course of an application. Dialogs may be displayed in a modal way where they block input to the background windows while displayed, or in a non-blocking modeless fashion. Aria allows you build dialogs from pages just as you would build any other pages.

Creating a dialog

A new dialog can be created from the File menu by choosing File | New | Aria | Aria Dialog . Alternatively you can right click on the pages folder in the file view and choose New Aria Page... from the popup menu.

When the dialog is displayed enter the name of the new dialog and choose Dialog from the Base class list.

Structure

In Aria a dialog is special case of a page, the Dialog class is derived from Page and inherits all its facilities just as any other page would. However, whereas a typical page is displayed in the body of the application or within a frameset a dialog is displayed in a popup window.

The Dialog class is not itself the dialog object from the point of view of the underlying Java system, instead the Dialog is a container nested inside a dialog class or window.

The Dialog class can be subclassed and in this way you can customize the look and feel of the dialog plus modify the dialog's behavior, perhaps setting icons and changing headers. Normally such customization is not undertaken in applications but in some highly branded application you may not want the default operating system color scheme clashing with you applications look.

Display

Just as a page is shown with the showPage method a dialog can be called with the showDialog method:

Showing a dialog

  1. pageMgr.showDialog( "myPopupDialog", translate( "MYDIALOG_TITLE" ), -1, -1 );

Where the last two parameters are the optional X and Y coordinates of the dialog, these coordinates can be set to that of the mouse or using the centre of the screen (pass a -2 as the X coordinate ). A dialog may be displayed by calling the showDialog method instead of the showPage method.

Displaying a dialog

  1. try {
  2. Dialog popupDialog = ( Dialog )pageMgr.loadPage( "myPopupDialog" );
  3.  
  4. Point pt = (( MouseEvent )getCurrentEvent() ).getPoint();
  5. Point topLeft = myObject.getLocationOnScreen();
  6. pt.translate( topLeft.x, topLeft.y );
  7. popupDialog.setLocation( pt );
  8. popupDialog.setModal( true );
  9. popupDialog.pack();
  10. popupDialog.showDialog( this );
  11. }
  12. catch ( Exception e ) {
  13. e.printStackTrace();
  14. }

A dialog will normally be displayed in the centre of the screen, but the location can be changed with a call to setLocation . The above example displays the dialog just below and to the right of the component ` myObject ' when the mouse is clicked on the component. (The translate method is used to adjust the current location since the current mouse position is relative to the component).

Once a modal dialog has been displayed the calling method is blocked till the dialog is dismissed. A dialog can be dismissed or closed by calling the closeDlg or cancelDlg methods.

For a modeless dialog it is sometimes useful to know when the dialog is dismissed even if the calling code is not blocked. The showDialog method therefore comes in a version that takes a reference to a callback method. The callback method is simply specified as the name of a response/handler method in the calling class. When the dialog is dismissed the callback method is invoked.

Packing

The decorations on a dialog (the border and header) can vary from platform to platform and it is therefore awkward to calculate the exact size of the dialog prior to display. Aria provides the pack method to resize the dialog so that all the components are visible. In addition the dialog page may specify a padding attribute to provide some extra space about the edges of the dialog and so that the components are not butted up against the edge of the dialog.

Padding a dialog

  1. <Page resource="text_cm" class="org.formaria.awt.Dialog" padding="5">

The above example shows the declaration of a dialog in XML as mentioned above in Structure.. It is important to note that a dialog must be of class Dialog or a class derived from the Dialog class (in either the org.formaria.swing.Dialog or org.formaria.awt.Dialog variant). The Dialog class is a special form of page designed for use in Dialogs. Once the super class is defined, the content of the dialog can be setup like any other page.

Modality

A dialog can be display in a modal way where all input to background windows is blocked till the dialog is dismissed. To make a dialog modal simply call the setModal method for the dialog class.

Making a dialog modal

  1. popupDialog.setModal( true );

Conversely a non- modal or modeless dialog can be created by setting this flag to false . A modeless dialog will allow user input to background window while the dialog is visible. Sometime modeless dialogs are used for parallel or alternate input mechanisms or for displaying summary or optional information in a separate window.

Other issues

Dialogs introduce a few additional issues due to the way they can interact with the rest of the system. The following sections cover some of these issues.

Setting the dialog location

The dialog is normally displayed at a position which centers it in relation to the the application. If you wish to explicitly set the location you can use a different version of the showDialog method:

Specifying the dialog location explicitly

  1. Dialog popupDialog = ( Dialog )pageMgr.loadPage( "MyPopup" );
  2.  
  3. popupDialog.setModal( true );
  4. popupDialog.pack();
  5. popupDialog.showDialog( this, "My Dialog", new Point( 100, 100 ) );

Centering the dialog on screen

In another variation of dialog positioning you can use a helper method to position a modeless dialog on the center of the screen. A helper class is provided to do this:

Centering a modeless dialog

  1. Dialog popupDialog = ( Dialog )pageMgr.loadPage( "MyPopup" );
  2.  
  3. popupDialog.setModal( false );
  4. popupDialog.pack();
  5. popupDialog.showDialog( this, "My Dialog", null );
  6. AriaUtilities.centerOnScreen( popupDialog );

Resizable dialogs

The dialog has a resizable property which an be set via the setResizable prior to display of the dialog. However, this option only works if native headers (decorations) are used for the dialog. The headers can be set with the setUseNativeHeaders method. Also note that if you are using a resizable dialog that you should use layout managers in the dialog or else resizing will not resize the dialog's content.

Titles

The dialog title can be set with the call to showDialog as above or via a call to setCaption . Within the XML the title can be specified as an attribute of the Page element.

Setting the dialog title

  1. XSwingHelper.showDialog( this, "MyPopup", "My Title", true, new Point( 100, 100 ));

Helpers

The dialog can also be displayed with the helper SwingHelper , or the AWT version AwtHelper .

Using SwingHelper to show a modal dialog.

  1. <Page class="org.formaria.test.Customer" title="Customer Details">

Focus

Displaying a dialog will cause the focus to shift to the new dialog window once it is displayed. Therefore it is important to exercise caution when using focus event and handlers that trigger dialogs. In some cases this can prove problematic and a special method is available for suppressing the focus events temporarily, however it should be used with caution and should be well tested as it can be difficult to get the timing of such changes right.

Turning off focus event handling

  1. getEventHandler().suppressFocusEvents( true );

Page activation

A dialog acts as any other page and will received the normal pageCreated , pageActivated and pageDeactivated messages. However the page from which the dialog display was triggered is not affected by the dialog display (other than by the focus events described above).

The bound components of the triggering page are however updated when a dialog is dismissed.

Data bindings

If a dialog page has bindings to the same part of the model as the current page in the application then special measures need to be taken to ensure that the data is displayed and saved in a consistent way. Take for example the following page which shows a dialog:

The calling page declaration

  1. <Page class="org.formaria.test.Customer">
  2. <Components>
  3. <Label x="154" y="136" w="79" h="20" style="Caption" content="Firstname:"/>
  4. <Edit name="firstnameText" x="241" y="136" w="238" h="20"/>
  5. <Button name="dlgButton" x="260" y="230" w="100" h="20" content="Open Dialog"/>
  6. </Components>
  7. <Events>
  8. <Event method="openDialog" target="dlgButton" type="ActionHandler"/>
  9. </Events>
  10. <Data>
  11. <Bind target="firstnameText" source="cust/firstname" output="cust/firstname"/>
  12. </Data>
  13. </Page>
  14.  

And the popup dialog which reproduces some of the input is as follows:

The dialog page declaration

  1. <Page class="org.formaria.test.CustDlg">
  2. <Components>
  3. <Label x="10" y="10" w="80" h="20" content="Firstname:"/>
  4. <Edit x="100" y="10" w="100" h="20" name="firstnameText"/>
  5. <Button x="30" y="60" w="50" h="20" name="OKBtn" content="OK"/>
  6. <Button x="100" y="60" w="50" h="20" name="cancelBtn" content="Cancel"/>
  7. </Components>
  8. <Events>
  9. <Event method="okClick" target="OKBtn" type="ActionHandler"/>
  10. <Event method="cancelClick" target="cancelBtn" type="ActionHandler"/>
  11. </Events>
  12. <Data>
  13. <Bind target="firstnameText" source="cust/firstname" output="cust/firstname"/>
  14. </Data>
  15. </Page>

Both the customer and the dialog page have declared bindings for their respective firstnameText text components to the same path in the data model. This means that the firstnameText component in the main application needs to display the results of the dialog if OK was clicked. So the Customer class openDialog function should look like the following.

The openDialog function

  1. public void openDialog()
  2. {
  3. saveBoundComponentValues();
  4. Dialog dlg = (Dialog)pageMgr.loadPage( "CustDlg" );
  5. dlg.pack();
  6. dlg.showDialog( this );
  7. }

The first line of this function makes a call to the saveBoundComponentValues function of the Page in order to persist all of the screen information to the data model. This ensures that when the the dialog is displayed it will be consistent with the customer page.

A similar situtation arises when the dialog is dismissed. Normally the dialog just disappears and you do not need to worry about the dialog's data. However if you are binding data as described above you will need to save the data when the dialog is dismissed and update the page that had displayed the dialog. The CustDlg class which subclasses the Dialog class follows.

The CustDlg class

  1. public class CustDlg extends Dialog
  2. {
  3. private boolean okClicked = false;
  4.  
  5. public void pageActivated()
  6. {
  7. okClicked = false;
  8. }
  9.  
  10. public void okClick()
  11. {
  12. okClicked = true;
  13. closeDlg();
  14. }</P>
  15. </TD>
  16. </TR>
  17. <TR>
  18. <TD ROWSPAN="1" COLSPAN="1">
  19. <P>
  20. public void cancelClick()
  21. {
  22. closeDlg();
  23. }
  24.  
  25. public void saveBoundComponentValues()
  26. {
  27. if ( okClicked )
  28. super.saveBoundComponentValues();
  29. }
  30.  
  31. }

When the OK button is clicked it is necessary to persist the bindings to the model. This is done by overloading the saveBoundComponentValue function and only calling the super function if the okClicked flag is true.

Fortunately the dialog class provides the setSaveOnClose(true) method to do exactly the above and saves you some code. The method causes the dialog to save its data and update the calling page if the OK button is used to dismiss the dialog. The saveOnClose flag is set to true by default so you do not need to modify the openDialog method.

The dialog still needs to know when the cancel button is clicked (assuming you have a cancel button on your dialog) and the method cancelDlg can be used to dismiss the dialog without saving the data. The modified page is as follows:

The dialog page with the calls to closeDlg and cancelDlg

  1. <Page class="org.formaria.test.CustDlg">
  2. <Components>
  3. <Label x="10" y="10" w="80" h="20" content="Firstname:"/>
  4. <Edit x="100" y="10" w="100" h="20" name="firstnameText"/>
  5. <Button x="30" y="60" w="50" h="20" name="OKBtn" content="OK"/>
  6. <Button x="100" y="60" w="50" h="20" name="cancelBtn" content="Cancel"/>
  7. </Components>
  8. <Events>
  9. <Event method="closeDlg" target="OKBtn" type="ActionHandler"/>
  10. <Event method="cancelDlg" target="cancelBtn" type="ActionHandler"/>
  11. </Events>
  12. <Data>
  13. <Bind target="firstnameText" source="cust/firstname" output="cust/firstname"/>
  14. </Data>
  15. </Page>

Now, since there is no need to set a special flag for the cancel event we can get rid of the rest of the custom code and just call the closeDlg method in response to the OK button click.

The dialog has one more button, the close button in the top right of the toolbar. This button is treated as the Cancel button and automatically dismisses the dialog without saving any data.

Localization

Aria includes the ability to render an application in different national languages and allows the user to change the language with relative ease and provides built-in tools to assist the translator in localizing an application.

The translation mechanism

Aria uses Java's resource bundles mechanism for translation of text. Whenever a page is constructed the component factory attempts to translate the text belonging to each component as it is constructed.

The resource bundles are property files that contain a list of keys and strings. Aria uses the text or content of each component as the key and attempts to look to the key in the resource bundle.

The resource bundle for each page can be specified in the page XML by including a ' resource ' attribute in the Page element. The attribute names the resource bundle to be used for the page.

Like most files the resource bundle is loaded by the project and it maintains a list of resource bundles. When a page is being constructed and components are added via the component factory in the normal way the component factory obtains a reference to a resource bundle and looks up the translations for whatever text is required.

Therefore when specifying the content for a component via XML or Java, the text for a multilingual application should be the key text for the string in the resource bundle. Once this is done you will need do very little more to translate an application.

Choosing the language

If this resource tag for a page is specified using an evaluated attribute the resource bundle can be configured dynamically throughout the application lifecycle. Alternatively an application can explicitly change the resource bundle used by the component factory to force a change in languages.

Whatever mechanism is used to change the resource bundle, the pages must be reloaded in order to effect the change in languages. It is also a good idea to prompt the user for the language selection.

Setting the resource attribute for a page

In order to show the mechanisms involved in setting the language for an Page it would be useful to show an example.

A basic page using a resource

  1. <Page resource="en">
  2. <Components>
  3. <Label x="10" y="130" w="400" h="30" content="GREETING"/>
  4. </Components>
  5. </Page>

This file shows that the resource attribute is set to "en" so the en.properies file will have to reside in the resource directory. The contents of that file are.

The en.properties file

  1. GREETING=Hello
  2. BYE=Goodbye

When run, the application will start with the page containing the text `Hello' which is read from the en.properties file.

In order to set an application wide language without having to reset the language on each page set the resource attribute to blank for each of the pages which need to be translated. This is an important point which can cause confusion. The resource attribute must not be removed but left blank in order for this to work.

Set the resource attribute to blank

  1. <Page resource="">
  2. <Components>
  3. <Label x="10" y="130" w="400" h="30" content="GREETING"/>
  4. </Components>
  5. </Page>

The default language can now be set in the startup.properties file with the following entry.

Setting the default application language

  1. Language=en

Now if a new language is required its a simple matter of creating the resource file for the language and specifying it in the startup.properties. So if French, for example, is to be the default language, create the following properties file.

The fr.properties file

  1. GREETING=Bonjour
  2. BYE=Au Revoir

Set the ` Language ' startup property to ` fr ' and the first page will read ` Bonjour '.

Changing languages in the application

Of course, it is not sufficient to set the startup language and leave it at that. The application needs to be able change language according to the requirements of the user. To illustrate this the previous example can be extended.

Change the first page to include a button to change language and also to proceed to a new page.

The modified Welcome page

  1. <Page class="org.formaria.langsample.Welcome" resource="">
  2. <Components>
  3. <Label name="testLbl" x="10" y="130" w="400" h="30" content="GREETING" opaque="true" style="Stripe"/>
  4. <Button name="language" x="10" y="180" w="400" h="30" content="Change Language" style="base"/>
  5. <Button name="continue" x="10" y="230" w="400" h="30" content="Next" style="base"/>
  6. </Components>
  7. <Events>
  8. <Event target="continue" type="ActionHandler" method="next"/>
  9. <Event target="language" type="ActionHandler" method="changeLang"/>
  10. </Events>
  11. </Page>

The two push buttons have ActionHandler events associated with them so the ` org.formaria.langsample.Welcome ' class needs to be created to implement these handlers, we can see this below:.

The implementing class for the Welcome page

  1. package org.formaria.langsample.Welcome;
  2.  
  3. import org.formaria.aria.*;
  4. import org.formaria.langmgr.LanguageChooserDialog
  5.  
  6. public class PosScreen extends Page {
  7.  
  8. public void changeLang()
  9. {
  10. saveBoundComponentValues();
  11.  
  12. LanguageChooserDialog chooser =
  13. new LanguageChooserDialog(
  14. project.getStartupParam( "Language" ));
  15. chooser.pack();
  16. chooser.showDialog( this, null );
  17. String language = chooser.getSelectedLanguage();
  18. if ( language != null ) {
  19. // Set the startup parameter for subsequent pages.
  20. project.setStartupParam( "Language", "en" );
  21.  
  22. //Unload all of the cached pages
  23. pageMgr.reset();
  24.  
  25. // reload the current page or reset/reinitialize the page if necessary.
  26. pageMgr.showPage( "Welcome" );
  27.  
  28. updateBoundComponentValues();
  29. }
  30. }
  31.  
  32. public void next()
  33. {
  34. pageMgr.showPage("NextScreen");
  35. }
  36. }

The changeLang method sets the new language, then clears the cached pages in the PageManager and finally reloads the Welcome page. The method uses a LanguageChooserDialog , but this dialog is optional and you can use other means to identify the desired language just as well.

The next() method is switching pages to the NextScreen page so that also needs to be created.

The NextScreen XML definition

  1. <Page resource="">
  2. <Components>
  3. <Label name="testLbl" x="10" y="130" w="400" h="30" content="BYE"/>
  4. </Components>
  5. </Page>

This page refers to the other key in the resource files. In order to test the functionality set the Language property in the startup file to fr and start the application. The first screen will read Bonjour . Click the continue button to get to the next screen and it will read Au Revoir .

Now restart the application and this time click the Change Language button. The text will change from Bonjour to Hello . Click the continue button again and the text on the next page reads Goodbye .

It is now a simple matter of replacing the functionality described with a list of languages or country flags for the user to set the language.

Localizing the Language Chooser

The language chooser itself can be localized so that the button texts and captions can be presented in the current language. The language chooser uses the very same method for localization as described above and uses three language keys:

  • ARIA_SELECT_LANGUAGE,
  • ARIA_CANCEL,
  • ARIA_OK .

To localize the dialog just add the translations for these three keys to your language file.

Scanning a project with the language editor

[This is a feature of Aria Pro]

Aria includes an editor (The language editor.) that allows translators to easily work with translations. This editor is covered in detail in a separate chapter.

One of the key aspects to working with the language editor and with localization in general is to prepare the resource bundles for translation. Rather than forcing the application developer to constantly interact with the localization facilities Aria's language editor provides a means of scanning an application for text. Using this option all pages within the project are scanned and any text that is not already part of the translation setup is added to the list of strings for translation.

Distribution of language files

Aria's language files are largely standalone and can be distributed on an individual basis so that the download for one user is not burdened with language files that may be of little or no use to an individual. Language files may also be swapped for branding and customization reasons or whenever a new or updated translation becomes available.

Encoding issues

The issue of character encoding inevitably arises when working with localized files. In Aria the language strings are stored in properties files. These files must be encoded in an encoding that matches your applications encoding. Aria by default uses UTF8 encoding and this should be sufficient for most language. The default encoding is controlled by a startup parameter, DefaultEncoding , which can be changed manually if needed.

Another significant issue you may have when working with localized files is the patchy support for character encodings in some editing tools. Some editors claim to save in a particular encoding but for one reason or another fail to do so consistently. Therefore we recommend testing all the tools your localization team uses prior to doing a full scale localization.

Locale specific properties

When a language resource bundle is loaded it is possible to set some locale specific to control how the text is loaded and rendered.

The EncodedLanguageResourceBundleLoader class attempts to load a file with the name .property_encodings to set these properties. The file can contain values for the encoding to be used to read the .properties file corresponding to the current language.

To install this resource bundle loader add the following to your code prior to loading any resource bundle:

Setup the resource bundle loader2

  1. currentProject.setResourceBundleLoader( new EncodedLanguageResourceBundleLoader( currentProject ));

Then, for each language a .property_encodings file can be created, so for example a language XX can have the following additional properties.

XX.property_encodings

  1. encoding=UTF16
  2. font_arial=NimSum
  3. font_arial10=NimSum;12

The Language Editor

A quick tour

The language editor is a simple to us facility for editing and localizing the text used within an application. At the core of the editor are two lists of strings, the lists are termed the source and target, each can be a different language. The basic idea is to pick a string from the source language and then enter the translation. The translation is entered in the editor window in the bottom right of the application. So in theory, the process of localizing an application is just a matter of iterating through the list of strings and entering the text of the translations.

Going beyond the most basics translation you will almost certainly need the support of language experts and the language editor can use some additional information such as keywords (for use by the application programmer), string IDs (again for use by the programmer) and hints to give the translator some contextual information about the string being translated.

The language editor also provides some support for the import and export of language files and resources so that the work of translation can be continued outside of the Aria environment.

Elements of the language editor

Below is a screenshot showing the language editor

The key elements of this editor are:

The language editor's key elements

Toolbar

The toolbar contains a number of buttons that allow you to import, export and add strings. You can also sort the string alphabetically or numerically and select the languages.

Source language list

The first dropdown list in the toolbar allows you choose the source language, that is the language from which you are translating.

Target language list

The second dropdown list in the toolbar allows you choose the target language, that is the language to which you are translating.

Source language

This list, displayed on the left hand side of the editor contains all the strings or pieces of text that the language editor knows of. The list also shows the index of the string within the resource.

Target language

The target list contains a list of strings in the translation or target language. Initially this list may be empty if not strings have been translated.

Preview area

The preview area shows the string as it will appear, complete with any substitutions. The preview area will also show a gray background for part of the string if the translation exceeds the length of the source string.

Editor's hints

The hints area (in the bottom left) shows the translator's hints and these may include information about the string's context, information that is important if the text is not enough to allow an unequivocal choice of translations.

Editor field

This field in the bottom right of the editor is where you can enter your translations.

Choosing the languages

Now that the language resources have been opened the language lists have been filled with the reference and the English languages. In many cases the reference language is only of interest to the programmer and may contain what might seem like cryptic codes. For a normal translator it is probably easier to translate from one natural language to another, for example from English into French.

The two lists shown are termed the source and target lists. Translation operates by choosing a string in one language and entering the translation for target language. Note that unless you are adding new strings the source language is unaffected by any translations you enter, changes are made to the target language.

The main menu should now have two new options for the Source and Target languages. Each of these menus contains a list of all the possible languages. Choosing from the source menu will select the source language and the left hand list will be updated to show this language. The title of the source list will also show the selected language's name. Similarly choosing a language from the Target menu will change the right hand side list.

If you are following this tour choose English as the source language and French as the target language.

Translation

To translate a string all you need to do is find it in the source language or in the target language. The source language also lists the ID of the string and in some situations it may be useful in locating the relevant string.

The hints shown at the bottom left of the language editor may also be of use in locating a string if two or more strings are similar. In such situations the hint may give useful information about the context of the string.

Once a string has been chosen it is shown in both the translation area and the preview area.

The preview area shows the string as it will appear in the application (although the font and line breaks will differ). The preview area shows the text following any substitutions or replacement of keywords and line breaks.

The translation area at the bottom right is an editor where you can type in the text of the string in the target language. That's it!

Spell checking

Before you finish with a string you may want to check the spelling. The language editor includes a spell checker, it can invoked by choosing the Language | Check Spelling... menu option or by pressing the CTRL+L keys.

If there is a spelling error or a word cannot be found in the spell checker's current word list then the dialog on the right will be shown.

The spell checker operates as you would expect. The suggestions list shows words that are similar to the unrecognized word and the buttons on the right of the dialog allow you to change the spelling or add the word to the dictionary.

For a technical application not all words may be found in the dictionary unless it has been customized. Some strings may also contain keywords or other idioms used by the programmer and these will not be found in the dictionary (and they probably shouldn't be added either).

When the spelling has been corrected click the Change or Change All buttons to update the translation.

Setup

The language editor can operate with a number of different file formats or by connecting to an SQL database. If you are planning to work with files you need to do little configuration, however if you plan to work with databases you will need to configure the database access.

Database configuration

The language editor relies on a JDBC database connection. In order to configure this connection you must ensure that the required drivers are available. You must also ensure that the necessary permissions are available to the user. Since the configuration parameters will depend largely on the database you are using you should consult your database documentation for the necessary details.

In many cases you will be able to connect via an ODBC driver. An ODBC driver is installed by default with Java and all you need to do is to ensure that the ODBC DSN has been configured. You can do this form the Windows control panel.

ODBC is limited to the Windows platform and to intranets. If you need to operate on another platform or via the Internet then you should use a JDBC driver. The language editor includes predefined settings for ObjectWeb's rmi-odbc driver. This driver allows ODBC to be used over the internet but requires that you install additional software on a web server. For more details see the ObjectWeb site.

The language editor also includes predefined strings for Microsoft's SQL Server driver. The driver for SQL Server can be downloaded from the Microsoft site.

Connecting to a database

Once the database driver has been configured you can start the language editor and attempt a connection. A connection is initiated by choosing the File | Connect... option or by pressing the connection button on the toolbar. Then the following dialog appears.

The dialog allows you to enter the settings that control the driver and the connection.

Database parameters

Parameter

Usage

Driver

This is the name of the Java/JDBC class that implements the database driver. See your driver's documentation for details. For example for use of ODBC on a local machine or intranet this will be sub.jdbc.odbc.JdbcOdbcDriver .

Database (DSN)

This is the name of the database. For an ODBC DSN it will correspond to the database DSN (prefixed by 'jdbc:odbc:') as entered on the control panel.

Username

This is the user name used to access the database. You will need to obtain a user name from the database administrator.

Password

This is the password associated with the user name, again you will need to obtain this from the database administrator. This value is saved between sessions.

Languages Table

The table used to hold the description of the languages and the field used for each language within the Translations Table.By default this table is called CS_LANGUAGES

Translations Table

The translations table hold the text of the various translations. Each language has a field of its own. By default this table is called CS_TRANSLATION

Once you have filled in the correct information click OK to make the connection. After a few seconds (depending on the size of the language tables) the source and target lists will be filled with the set of strings from the languages. On the left, the source list will show the reference language while the right hand list will show the English translation (or the first proper language in the translation set).

The formats of these tables are described in the formats sections

Starting a file based session

A file based session can be started at any time by choosing the File | Open... menu option, or by clicking on the Open button on the toolbar.

The open file dialog is shown adjacent.

To start a translation session use the dialog to locate a language list file. The language list file contains a list of the files that make up a language set. Once the file has been chosen the language files are opened and the source list shows the reference language while the target list shows the first available language which is normally english.

When using a file based session any new languages will cause a new language file to be created in same the directory as the language list file.

When using a file based session it may also be important to ensure that the source language is the reference language if new strings are to be added to the language and if the application programmer intends to use the string IDs in the coding of the application. The reference language should be considered as a master language and therefore it is important to ensure that it contains an entry for every string. In other languages this is no so important as a default translation may be provided.

Choosing languages

Once the language set has been opened regardless of the data store used you will be presented with two lists of strings. These two lists are termed the source and target languages. The main menu contains a menu for each from which you can choose the languages to be displayed.

The source menu allows you to choose the language displayed in the left hand side source language list, while the target menu allows you to choose the language for the target list.

As you change the languages the caption of each list is changed to show the associated language.

The source list also shows the ID of each string.

Configuring the spell checker

The language editor includes an open source spell checker. This spell checker can be configured to work with dictionaries for a variety of languages. These dictionaries take the form of a compressed (zipped) word list. Word lists for many languages can be found on the internet from the likes of the ASpell project.

The spell checker by default works with english. If you want to use another language then select the language's word list file by choosing the Language|Spell check setup... menu option and choose the appropriate zip file.

Starting a new project

For a database based system
  1. Setup a new blank database
  2. Add the required tables using the following SQL statements
  3. CREATE TABLE [dbo].[CS_LANGUAGES] ( [ID] [int] IDENTITY (1, 1) NOT NULL , [Name] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL , [Code] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL , [Description] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY]
  4. CREATE TABLE [dbo].[CS_TRANSLATION] ( [ID] [int] NOT NULL , [Reference] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL , [Hints] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL , [EN] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY]
  5. Setup the ODBC DSN if the JdbcOdbc driver is going to be used
  6. Choose the File|Connect menu option and select the appropriate settings
For a file based system
  1. Choose the File|New ... menu option
  2. Navigate to a new folder and enter the langlist.lst as the name of the language list.

Opening a file

A file based session can be started at any time by choosing the File | Open... menu option, or by clicking on the Open button on the toolbar. The open file dialog is shown adjacent.

Adding a language

To add language you must have opened a set of language resources. Then all you need to do is choose the Language|New Language... menu option. The following dialog then appears.

The code is used to name the language file and should be a two or three letter code. The ISO language code is normally a good choice.

The name of the language can be whatever you like but should preferably be in the language itself. Some applications present a list of languages to the user and therefore show the language name to the end user.

Adding a new String

A new string can be added to the language by choosing the Language|New String menu option or withe CTRL+W keystroke combination.

Once the new string has been added some dummy text is inserted and displayed in the translation editor at the bottom right of the application.

Adding a translation

To translate a string all you need to do is find it in the source language or in the target language. The source language also lists the ID of the string and in some situations it may be useful in locating the relevant string.

The hints shown at the bottom left of the language editor may also be of use in locating a string if two or more strings are similar. In such situations the hint may give useful information about the context of the string.

Once a string has been chosen it is shown in both the translation area and the preview area.

The preview area shows the string as it will appear in the application (although the font and line breaks will differ). The preview area shows the text following any substitutions or replacement of keywords and line breaks.

The translation area at the bottom right is an editor where you can type in the text of the string in the target language. That's it!

Sometimes the target language may not contain any translation for the string and in such situations it is best to find the translation in the comments language.

The language editor includes a menu option to add all strings from the source language to the target language where the target language does not contain a translation. This can be useful if you wish to provide a default for the string even if you do not intend to translate it immediately.

Substitutions

Substitutions are a special feature of Formaria's language editor libraries and are not relevant if you are using the text resources in some other way.

A string can include a word or phrase that appears in the reference language and delimited by special characters. Such pieces of text are referred to as substitution strings. Substitutions are made by looking up the keyword and replacing it with the translation of that keyword. For example, one string might contain

A simple substitution in its raw form

  1. Will the real slim_shady please stand up

here the keyword slim_shady is delimited by the characters. Supposing that the language contains an entry for the keyword such that it would be translated as ' Marshall Mathers ', then the text would appear to the end user as

The processed string, complete with substitution.

  1. Will the real Marshall Mathers please stand up.

Special substitutions

When the language files are saved as files then any carriage returns line feed (CRLF) pairs or newline characters must be replaced so that they do not cause problems in storing the data. For this purpose the language editor uses the CRLF keyword to substitute for the CRLF pair.

Remote translation

In some situations a translator may not be able to or wish to connect to the main database holding the languages. In such situations it is possible to export the language resources to a directory. The directory contents can then be sent to the translator via e-mail, disk or other means.

The remote translator can then open and update the file based language resource and return the file set to the person responsible for administering the language resources. The remote translator will not be able to add new strings or new languages.

The administrator can then import the updated files received from the remote translator. The administrator will be asked to confirm the merging of each language. In this way updates to an individual language can be loaded and other rejected.

File formats

The language editor can work with a number of file formats and can usually figure out which files to use. The language editor specific files are described below and these include some extra information used by the language editor to manage all the languages in your application. If these custom files are not present the language editor can read property files provided that they are located in the lang subdirectory of your project.

Using the property files in this way can be the quickest way to get started with the translation of an existing Java application.

Database table formats

The database tables are described below using the SQL CREATE TABLE statement.

Database language table setup

  1. CS_LANGUAGES
  2.  
  3. CREATE TABLE [dbo].[CS_LANGUAGES] ( [ID] [int] IDENTITY (1, 1) NOT NULL , [Name] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL , [Code] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL , [Description] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY]
  4.  
  5.  
  6. CS_TRANSLATION
  7.  
  8. CREATE TABLE [dbo].[CS_TRANSLATION] (
  9. [ID] [int] NOT NULL ,
  10. [Reference] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL ,
  11. [Hints] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL ,
  12. [EN] [nvarchar] (4000) COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY]

Each new language field is setup using the language code in uppercase as the field name and with a type of NVARCHAR(4000) .

Flat file formats

The Language Editor can use a set of text files to store the languages. The files are cross referenced via a language list ( .lst ) file. The format is quite simple.

The language list (.lst) file

First the file lists the number of languages and then using one line per language it lists the languages. Each line specifies the language ID, the language (ISO) code and the language name. Each field is separated by the '|' character. An example listing two languages is shown below plus the reference language and the hints language. The reference and hints language should be specified as shown in the example.

The language list

  1. 4
  2. 0|Reference|Reference
  3. 1|Hints|Hints
  4. 2|EN|English
  5. 3|US|US-English

The language (.jln) file

The language file contains the strings belonging to an individual language. The language file first lists the number of strings in the language and then using one line per string it lists the string. Each line contains the string ID followed by the text of the string. A fragment of a language file is shown below.

Note that the example shows some strings containing special words delimited by the '' character. These delimited words can be substituted by text for other texts. The delimited text normally is a keyword found within the language. Some special substitution keywords are used, for example CRLF is used to mark a point where a carriage return-line feed pair should be inserted (since the convention of one line per string cannot on its own support multiline texts).

Sample language file

  1. 855
  2. 863|No matching valve was found, a balanced port valve or a larger connection may be available instead
  3. 862|Strainer
  4. 861|WARNING: Due to range limits some input values have changed. Press the forward button again to continue
  5. 860|No matching valve was found, a single port valve or a smaller connection may be available instead
  6. 859|Application:CRLFD: Temp. controlled defrostCRLFF: Fan controlCRLFA: Alarm relay
  7. 857|Auto
  8. 856|ODF Solder 1/4 in.
  9. 855|Flare 6 mm
  10. 854|CutRing 6 mm
  11. 853|ODF Solder 6mm
  12. 852|Flare 1/4 in.
  13. 848|A = AlarmCRLFD = DefrostCRLFF = Fan
  14. 847|PTC 1000 Ohm sensor
  15. 846|Pt 1000 Ohm. sensor

The substitution (.sub) file

The format of the substitution file is exactly the same as the language file (.jln). In fact it is a subset of the reference language. The substitution file contains only those strings that start and end with the delimiter character mentioned above.

Property files

The property files (.properties) produced when saving flat files are largely similar to the .jln files described above. Each string is recorded with the ID being used as the keyword.

Exporting to Microsoft Excel

When exporting languages it is possible to generate an Excel workbook for the languages. Sometimes this can be useful as just about everyone is familiar with Excel. Export to Excel makes it easy to distribute the language files for translation even though the language editor can run as a standalone application.

When the export dialog appears and the desired export languages have been chosen, simply remember to check the Export Excel Worksheet checkbox.

An Excel workbook will be created with a worksheet containing a column for each language exported. Of course Excel doesn't really know anything of the relationship of the various fields and IDs so some care is needed if Excel users try to add strings to the language or if they delete cells. So, when re-importing the Excel sheet it is a good idea to backup the language files. It is probably also a good idea to keep a copy of the initial export for comparison purposes in case some conflict arises when importing the files.

Using the standalone language editor prevents some of these issues.

Running the standalone editor

The standalone language editor can be used without need for NetBeans or Eclipse, and can be used to edit the localization files.

TODO Describe the setup of the standalone editor

Redistributing the language editor

The standalone language editor is a commercial tool and you are not entitled to use it or redistribute it unless you have purchased a license from Formaria. Please contact sales@Formaria.com for more information, the license fee is small.

Data Management

Data management

Data is central to most applications and many applications spend much of their code on manipulating data. Aria includes a powerful data modelling capability. The data model holds all data in a hierarchical structure much like a filesystem or an XML document.

Each piece of data in the model must provide a minimum interface so that it can be incororated into the data model. The requirements of this interface allow applications to access various parts of data model using a simple and consistent set of methods.

The abstract data model also allows applications to be oblivious to the actual data type and the implementation of the data type. For the application developer this greatly helps to decouple the user interface from the underlying data model.

In many instances the application need only know how to map data into the user interface. This mapping of data to the user interface or data binding is a very powerful feature of Aria applications. In earlier chapters Data binding.) we saw how data binding could be used to connect data to the user interface and in this chapter we will show how to get that data into the application in the first place.

Types of data

Aria provides assistance in loading and managing an application's data and this data may fall into several different categories. The main distinction between different types of data is in how the framework processes the data.

For the simplest data access the framework does nothing other than assist in loading the files. A wide variety of data can fall into this category including things that you may not otherwise regard as `data', for example images, text files, HTML and so on.

The next kind of data is loading in much the same way but gets processed during the loading process. Typical of this is the static model data that we have already encountered. Other files of this mature include the data bindings, validation rules, localization files and the various configuration files used by an application. In loading these files the framework transforms the data into more usable structures.

The third type of data and the type that this chapter is most concerned with is data that originates in databases of one form or another. Aria includes some special features to make working with and integrating database data as simple as possible.

Aria can also handle a wide variety of other data sources via the routes and services mechanism but that is covered in a dedicated chapter (Introduction to routes and services.).

Resource access

The Aria project provides a means of loading files from the classpath and from Jar files. The project also hides some of the details of building input and output streams. Typically a number of predefined subdirectories including pages, resources, lib and lang are used to load files. The API provides access to these resources without the need to specify these directories in the file path. The project can also use additional ClassLoader s if so desired or where special circumstances dictate. The project API also provides access to startup parameters.

Many of the components within Aria reference the project API while accessing files and other resources (The API includes special image loading methods). The API helps simplify access as the components do not need to know how to search the file path(s) or classpaths(s).

The data model

The popular Model-View-Controller is used heavily in Aria. The MVC architecture has already been discussed in connection with data binding (see Data binding.), so we will not reiterate how it is implemented in Aria. However the data model can be used outside of the context of data bindings and we will focus on this briefly.

The data model in Aria is a hierarchical structure. Each node in the model is an instance of the DataModel interface. The interface provides access to a set of attributes and a set of child elements and this makes it possible to address the nodes with the XPath like syntax that we have already seen.

There can be many different implementations of the DataModel interface and therefore not all data within the model needs to be structured in this way. For instance a node can internally implement a completely different arrangement of data as it is only the connection of the node to the model that is governed by the DataModel interface.

The DataSources

As the data model is a loosely coupled arrangement of what might be disparate types the mechanism for loading data also needs to be flexible. As we have seen in earlier chapters the data for the model can come from numerous sources each listed in the files pointed to by the startup property ModelData .

The basic DataSource class ( org.formaria.DataSource ) loads the static data and can process some simple table structures. The nodes loaded by this data source are of the default type for the model, the BaseModel class and each can have an arbitrary number of attributes and children.

Static data

Typically an application with include some static data for things like populating lists and perhaps for provision of initial or default values.

The aforementioned startup file points to an XML files that lists the data sources via the 'ModelData' entry, which in turn the model data file will appear something like this:

A model data configuration file

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <DataSources>
  3. <DataSource name="ListValues" filename="datasources.xml"/>
  4. </DataSources>

An example of this static data is:

A sample dataset

  1. <Datasets>
  2. <dataset id="settings" value="admin">
  3. <list id="server1" value="set.Formaria.com">
  4. <server value="wizards" id="wizards">
  5. <item value="Add service" id="1" ui="AddService"/>
  6. <item value="Add mail user" id="2" ui="AddUser"/>
  7. </server>
  8. <services value="services" id="services">
  9. <item value="SMTP server, port 25" id="SMTP"/>
  10. <item value="POP server, port 110" id="POP"/>
  11. </services>
  12. </list>
  13. ...

Each node should have an id attribute as it is this ID that is used to identify the node with the model. If an ID attribute is not provide Aria with synthesize one and the data will still be accessible (although it may be more difficult to identify the nodes at first glance had an ID been provided).

Tables

The static data can also include HTML like tables and lists. These tables are often used to configure content for dropdown lists and the like in the user interface. You will numerous examples of such tables in the on-line examples and tutorials.

Finding data

Once the model is loaded we need a way to interact with it. The model provides a number of functions to query, update, add and remove nodes and attributes. To query the model the following can be used.

Retrieve the root model for the project

  1. BaseModel rootModel = ProjectManager.getModel();

This call retrieves a reference to the current project's root model node. This model will contain any information which might have been loaded from the dataset files specified by the ModelData startup parameter. In Page classes or derivatives the rootModel member variable is predefined for convenience.

Retrieving a model node

  1. DataModel model = ( DataModel )rootModel.get( "customer/firstname" );
  2. String firstName = ( String )model.get();

Here a model node at a specified path is being looked-up. Since the model can hold a variety of data types most of the lookup methods return the abstract Object type and some type casting is required to make use of the returned data objects. The get method in this way returns an object and must therefore be cast to an DataModel or an BaseModel object to be usable. Once retrieved the value attribute of the model can be retrieved by calling the get method without a parameter.

Iterating the children of a model

  1. DataModel model = ( DataModel )rootModel.get( "customer" );
  2. for ( int i = 0; i < model.getNumChildren(); i++ ) {
  3. DataModel child = ( DataModel )model.get( i );
  4. String name = child.getId();
  5. String value = ( String )child.get();
  6. }

This code fragment shows how to retrieve and iterate a model node and its children in order to get their names and values. The model is nearly always based or configured by XML files and following from this the id and value attributes are of special use as we have already seen (with the id attribute being used to help identify individual nodes).

Checking attributes

  1. DataModel model = ( DataModel )rootModel.get( "application" );
  2. int idx = model.getAttribute( "date" );
  3. String appDate = model.getAttribValue( idx );

A model node can contain any amount of attributes above and beyond the default id and value attributes. In order to obtain the value of a named attribute it is necessary to first get the index of the attribute and then to get the value of the attribute at that index. Setting values

  1. DataModel model = ( DataModel )rootModel.get( "customer/firstname" );
  2. model.set( "Joe" );
  3.  
  4. model = ( DataModel )rootModel.get( "customer" );
  5. for ( int i = 0; i < model.getNumChildren(); i++ ) {
  6. DataModel child = ( DataModel )model.get( i );
  7. int idx = child.getAttribute( "somedata" );
  8. child.setAttribValue( idx, "somevalue" );
  9. }

Values can be set for the model itself or for any of the attributes. Within the BaseModel node structure if the attribute does not yet exist it will be created automatically. This automatic creation of storage is very helpful when binding to a user interface as it means that you do not need to create and initialize a data structure for the associated model, instead you can just bind to the `imaginary' data structure in the knowledge that it will be created on demand. However it is important to remember that this functionality is derived from the BaseModel node and that it is not `injected' into other node types and that there is no `magic' at work here. For more information you can refer to the API documentation.

If necessary you can also create your own nodes, for example:

Appending one model to another

  1. DataModel model = ( DataModel )rootModel.get( "customer" );
  2. new BaseModel( model, "firstname", "Joe" );
  3. new BaseModel( model, "surname", "Bloggs" );

This code shows two new BaseModel nodes being appended to a parent node. The constructor takes care of assigning the id and value and also of added the model to the parent. Similarly you can remove nodes from the model:

Removing a child model node from its parent

  1. DataModel customerModel = ( DataModel )rootModel.get( "customer" );
  2. DataModel firstNameMdl = ( DataModel )customerModel.get( "firstname" );
  3. model.remove( firstNameMdl );

The above code shows how child nodes can be first referenced relative to the parent. you need not reference directly from the root node and instead you can reference with the relative path from some other node, lower in the node hierarchy. Once you have a node reference you can then manipulate the node, add a new node or as above remove a node from its parent.

Loading and saving files

While it is often useful and sufficient to load data at startup it may be necessary at times to open or save data files explicitly from Java classes after the application has first started. This can be done by using the DataSource class.

Persisting a model node to file

  1. import org.formaria.data.DataSource;
  2. ...
  3. public void save()
  4. {
  5. String contents = getModelText();
  6. saveFile( "C:\AriaApps\test.xml", contents );
  7. }
  8.  
  9. private String getModelText()
  10. {
  11. BaseModel customerModel = ( BaseModel )rootMdl.get( "customer" );
  12. StringWriter sw = new StringWriter();
  13. XDataSource.outputModel( sw, customerModel );
  14. return "<Datasets>" + sw.toString() + "</Datasets>";
  15. }
  16.  
  17. public void saveFile( String filename, String contents )
  18. {
  19. try {
  20. FileOutputStream fos = new FileOutputStream( filename );
  21. OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" );
  22. BufferedWriter bw = new BufferedWriter( osw );
  23. bw.write( contents );
  24. bw.flush();
  25. bw.close();
  26. }
  27. catch ( IOException ex ) {
  28. ex.printStackTrace();
  29. }
  30. }

The getModelText function uses the XDataSource class to output the contents of the customerModel node to a StringWriter in XML format. The text is wrapped in a Datasets node so that the saved file can easily be reopened by the application. The saveFile function outputs the text to a FileOutputStream . The saved data file will be saving in the following format.

The saved data file

  1. <Datasets>
  2. <data id="customer">
  3. <data value="Joe" id="firstname">
  4. <data value="Bloggs" id="surname">
  5. </data>
  6. </Datasets>

In order to reopen the datafile the DataSource class can once more be employed. Restoring a datasets file programatically

  1. import org.formaria.data.DataSource;
  2. import org.formaria.xml.XmlElement;
  3. import org.formaria.xml.XmlSource;
  4.  
  5. ...
  6. public void openFile()
  7. {
  8. String contents = getFileContents( "c:\AriaApps\test.xml" );
  9. restoreState( contents );
  10. }
  11.  
  12. private String getFileContents( String filename )
  13. {
  14. StringBuffer contents = new StringBuffer();
  15. try {
  16. FileInputStream fos = new FileInputStream( filename );
  17. InputStreamReader osw = new InputStreamReader( fos, "UTF8" );
  18. BufferedReader bw = new BufferedReader( osw );
  19. String temp = bw.readLine();
  20. while ( temp != null ) {
  21. if ( temp != null )
  22. contents.append( temp );
  23. temp = bw.readLine();
  24. }
  25. bw.close();
  26. }
  27. catch ( IOException ex ) {
  28. ex.printStackTrace();
  29. }
  30. return contents.toString();
  31. }
  32.  
  33. private void restoreState( String contents )
  34. {
  35. ( ( BaseModel )rootModel.get( "aria_state" ) ).clear();
  36. ( ( BaseModel )rootModel.get( "customer" ) ).clear();
  37.  
  38. StringReader sr = new StringReader( contents );
  39. XmlElement ele = XmlSource.read( sr );
  40.  
  41. if ( ele != null ) {
  42. XDataSource ds = new XDataSource();
  43. ds.loadTable( ele, rootModel );
  44. }
  45.  
  46. }

The getFileContents function simply reads the contents of the named file into a StringBuffer and returns its contents. The restoreState function clears the aria_state model node. This needs to be done if any of the databindings make use of the data being restored. The customer node is also initialized as this is the root of the node being restored. The file contents are then used to construct an XmlElement . The XDataSource class is then used to load the XmlElement into the specified model which in this case is the project's root model.

This mechanism provides a quick and simple way of managing projects and their data. For a fully working example of this you can refer to the introductory and advanced tutorials.

Other data types

While the static data by its nature is restricted in what it can hold the same restriction does not apply to the model itself. Each node in the model is a Java object and therefore each node can contain just about any type of data. Aria will wrap new nodes in an instance of BaseModel so that necessary query semantics can be supported by the actual value is flexible.

Not only can the model hold any type of data but the node types can also be varied. The nodes need only implement the DataModel interface to be included in the model.

The ModelData file provides the opportunity to list additional data sources. Such a data source should extend the XDataSource class.

Databases

Many applications rely on relational databases for their data, some are even little more than editors for database resident data.

A common style of application is one that presents forms based on a result set and allows interactive iteration through the result set. Visual Basic popularized this type of application and now Aria enables similar functionality for rich Java applications.

Support for database tables and result sets can be added to Aria through use of the optional database packages ( org.formaria.optional ). These packages allow JDBC result sets to be included in the Aria data model. The database model nodes can then be bound to UI components in the normal manner.

Setting up a database data source

In this section we will look at how an instance of the popular HSQLDB database can be setup within NetBeans.

First we must tell NetBeans about the JDBC database driver, so on the Runtime view expand the Databases | Drivers tree node and right click. Choose the Add Driver...

The driver configuration dialog appears and you can use the Add... button to browse for the hsqldb.jar file. NetBeans then recognizes the driver and you can click OK . Once the driver has been added you can again right click on the database node and choose New Connection... to popup the new connection dialog shown below:

HSQLDB has a number of conection modes and these vary the form of the Database URL. In the example above a connection to a database called my_db , located in the C:\CVS\myproject folder is added. In this standalone mode HSQLDB will create a new databasse instance if none already exists. The setup for other modes and other database is similar, but please consult the appropriate documentation for more details.

Standalone HSQLDB usage

The standalone mode of HSQLDB is of particular interest as using this mode it is possible to load the database from the classpath. The example above gave a full example of the database URL but by changing the URL to jdbc:hsqldb:my_db it would possible to place the C:\CVS\myproject folder on the classpath. In this mode HSQLDB will search the classpath for the named database giving you somewhat greater control over the location of the database. Indeed a read-only database could be located within a Jar file for delivery over the web, however in most cases even if the database is delivered as a Jar it would be necessary to extract the database to a local folder so that changes can be written to the database.

For development purposes it is probably best to add the database (be it HSQLDB or something else) in a server mode. A server mode will make the database less dependant on the application process and this can be an issue while debuging where stopping and starting of the application may occur abruptly (databases normally prefer an orderly shutdown so that they can write changes back to the store). Using a server mode may also leave more memory available for your application.

Whatever the mode, NetBeans provides some valuable tools once you have setup a connection. You can inspect the database fields, view the table data and execute commands.

Eclipse Plug-in and data management

Eclipse does not have the same sort of database facilities as NetBeans out-of-the-box, so we have used a third party module, the open source Eclipse SQL Explorer to provide similar functionality

The Eclipse SQL Explorer plugin is a thin SQL client that allows to query and browse any JDBC compliant database. The plugin (version 3.5) is supported by the AriaEditor Eclipse plugin - this allows to register and setup database tables in a Aria project's data model using drag and drop mechanism.

Installing the Eclipse SQL Explorer plugin.

1 Download the plugin from SourceForge (http://eclipsesql.sourceforge.net -version 3.5) and extract the zip file in the Eclipse installation directory.

2 Restart the Eclipse and open the SQL Explorer perspective .

3 Using the Connections View configure a new connection to the database containing tables which you want to register and setup in your Aria application.

Registering and binding database tables.

1 Open the Connections view and establish a connection to the database containing a table which you want to bind. The available database schemes and tables will appear in the Database Structure view.

2 Drag and drop the selected table from the Data Structure view to the Data node displayed in the Visualizer . The table will appear in the data model hierarchy and will be configured and registered in the current Aria application's data model.

3 Once the table is registered its data can be viewed under the Visualizer tab.

4 Drag and drop the table node from the Visualizer to a component that you want to bind it to.

5 As the table node is dropped you will be asked to configure some properties of the binding in the popup dialog (in most cases the default values will suffice).

6 Once the dialog is closed (by pressing OK button) the binding will be set and configured. The Data property of the component will get the specified value.

Addings the database DataSource

The first task in using database tables in Aria is to add an additional datasource so that your application can locate and load the database data and configuration.

The datasource is configured through the startup file with an entry as follows:

The startup.properties entry for the database datasource

  1. DataSourceClass=org.formaria.data.OptionalDataSource

This entry causes the OptionalDataSource to be loaded. The data source in turn reads another startup file entry, the ModelData entry to locate its configuration data, just as we have seen with the static only models presented above:

The data model configuration file entry for a database datasource

  1. ModelData=test_datasets.xml

The ModelData file in turn points to a set of data sets:

The datasources configuration

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <DataSources>
  3. <DataSource name="Tables" type="database" filename="test_tables.xml"/>
  4. <DataSource name="ListValues" filename="test_datasources.xml"/>
  5. </DataSources>

These data sets are then processed according to the type attribute. In the above example the ListValues data set is the same static data source that we have seen earlier. The Tables data set is of the database type and is therefore processed by the XOptionalDataSource class.

Configuration of an individual datasource

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Datasets>
  3. <Connection id="default" driver="org.hsqldb.jdbcDriver"
  4. url="jdbc:hsqldb:testdb" user="sa" pwd=""/>
  5. <ResultSet id="Voltages" conn="default" from="CS_VOLTAGES"/>
  6. <ResultSet id="Voltages2" conn="default"
  7. sql="SELECT DISTINCT FREQUENCY,PHASES FROM CS_VOLTAGES"/>
  8. </Datasets>

The entries in the file pointed to by the database entry (in the test_tables.xml file) configure a database connection and some tables. The tables are specified as a simple shortcut to accessing the database. The same table is referenced in two ways with the first retrieving all fields while the second specifies particular fields.

The tables listed are added to the model under the tables node and thus the Voltages table is located at the tables/Voltages node in the database.

Tables need not be preconfigured in this way, but it is the easiest way to access tables so that no extra code is required.

Adding tables with Drag and Drop

If you added you registered your database with NetBeans as described above you can add a table to your Aria application with drag and drop.

  • First open the Visualizer from the Aria menu within NetBeans.
  • Select the table of interest in the Runtime view
  • Drag and drop the table from the Runtime view to the Visualizer .

When the table is dropped it is registered and the configuration files are updated.

Binding to database tables

The simplest way to use tables is to bind UI components such as the Table, Table2 or Combo components (Table2 is a derivate of the Swing JTable class whereas Table is a built-in component that provides common functionality for AWT and Swing widget sets. XTable2 provides greater functionality at the expense of requiring the Swing library). In the case of the Voltages table above a binding can be specified as follows:

Binding to a database table

  1. <Data>
  2. <Bind target="myList" source="Voltages" output="tables/mylist" display="1" />
  3. <Bind target="yourTable" source="Voltages" output="tables/Voltages/yourtable"/>
  4. <Bind target="ourTable" source="Voltages" output="tables/ourtable"/>
  5. <Bind target="voltageEdit" source="Voltages" output="tables/voltageedit"/>
  6. <Bind target="voltageList" source="Voltages" output="tables/voltagelist" display="1" />
  7. </Data>

In this example we can see the same table being bound to various UI components. Note how the output field is used to dictate where the state information about each binding is saved. Note also the display attribute that controls the field that is displayed.

Binding tables with Drag and Drop

Using the data visualizer as described above you can select a table or some other type of data node from the data model hierarchy displayed in the visualizer and simply drag and drop the node to a component on a page. As the node is dropped on the component you may be asked to select some configuration properties depending on the component type and the data type, but mostly the default values will suffice.

If you do no wish to use drag and drop you can still use the hierarchy displayed in the visualizer to locate the dta node of interest and to find its path within the model. The path can then be copied to the clipboarrd and pasted into the Data property field of the component's property sheet.

Note that the data model may change as you run your application and load or unload datasources, pojos or even dynamically create data. The visualizer will operate in a debug mode and you can stop execution at a breakpoint and refresh the data model to get a snapshot of the model state at that time. This sort of design/debugging can be useful for complex models where it may not be easy or obvious to say where certain data resides, particularly for modular applications that have many distinct developers or where modules communicate via the model.

Accessing database tables

Within Java it is also possible to access the tables via the data model. The DatabaseTableModel class provides additional access methods and in the code below the class is used to implement a sort method (actually it relies on the SQL ORDER BY clause).

Retrieving a table and updating a UI component

  1. public class MyTable extends Page
  2. {
  3. Table myTable;
  4.  
  5. public void pageCreated()
  6. {
  7. myTable = (XTable)findComponent( "myTable" );
  8. myTable.setUpdateModelSelection( true );
  9.  
  10. myTable2 = (XTable2)findComponent( "yourTable" );
  11. myTable2.setUpdateModelSelection( true );
  12.  
  13. myCombo = (XComboBox)findComponent( "myList" );
  14. myCombo.doLayout();
  15. }
  16.  
  17. public void sort()
  18. {
  19. DatabaseTableModel completeVoltageTable = DatabaseTableModel.getTable( "Voltages");
  20. completeVoltageTable.setDistinct( true );
  21. completeVoltageTable.setOrderField( "ID" );
  22. completeVoltageTable.retrieve();
  23.  
  24. myTable.setModel( completeVoltageTable );
  25. updateBoundComponentValues();
  26. }

Distinct and other clauses

Tables often contain duplicates and the DISTINCT directive is used in SQL to eliminate such duplicates from a result set. The methods of the DatabaseTableModel class and other classes provide access to the particular clauses within a SQL statement and if you use such an access mechanism (rather than formulating the entire SQL statement yourself) you may need to explicitly set the DISTINCT directive as in the example above using the setDistinct method.

Setting the fields to retrieve

  1. public void filter()
  2. {
  3. DatabaseTableModel voltageTable = new DatabaseTableModel();
  4. // Set the query elements
  5. // FROM clause, FIELDS, WHERE clause
  6. voltageTable.setupTable( "CS_VOLTAGES", "VOLTAGE_DESCRIPTION, VOLTAGE_MIN, " +
  7. "VOLTAGE_MAX", "FREQUENCY=50" );
  8. voltageTable.retrieve();
  9. myTable.setModel( voltageTable );
  10. updateBoundComponentValues();
  11. }

Again using this feature we can set individual clauses:

Setting individual SQL clauses

  1. String fromClause = "LEFT_TABLE INNER JOIN RIGHT_TABLE ON LEFT_TABLE.FIELD_A =
  2. RIGHT_TABLE.FIELD_1";
  3. String fields = "FIELD_A, FIELD_B, FIELD_2, FIELD_G;
  4. String where = "(FIELD_3 LIKE ('"+someValue+"'))";
  5. String order = "FIELD_A";
  6.  
  7. DatabaseTableModel valveTable = new DatabaseTableModel();
  8. valveTable.setupTable( fromClause, fields, where );
  9. valveTable.setOrderField( order );
  10. valveTable.setDistinct( true );
  11. valveTable.retrieve();

Finally when this isn't enough you can set the entire SQL statement using the setSqlStatement :

Set the entire SQL statement

  1. String sql = "SELECT DISTINCT foo, bar FROM snafu";
  2. String connName = null; // Use the "default" connection
  3.  
  4. DatabaseTableModel valveTable = new DatabaseTableModel();
  5. valveTable.setSqlStatement( sql, connName, false );
  6. valveTable.retrieve();

Filtering and finding rows

A table can be filtered or refined in a sort of drill-down mode using the findRows method. The method builds a query by augmenting the parent query with a WHERE clause:

Drill down using findRows

  1. String where = "(A='foo') AND (B='" +someValue+"')";
  2.  
  3. DatabaseTableModel myTable = new DatabaseTableModel();
  4. myTable.setupTable( "TABLE_A", "A, B, C", null );
  5. myTable.setOrderField( "A, B, C" );
  6.  
  7. DatabaseTableModel selectedTypes = myTable.findRows( where );

Prepared statements

Not all database access can be setup via simple predefined and explicit SQL queries, in some cases it is necessary to parameterize the queries and PreparedStatements are used for this purpose. PreparedStatements are also used when the data or parameters being passed as part of the query cannot conform to the normal encoding rules for SQL statements. Normally SQL allows only ASCII characters and even then there are limits to what can be used for parameters or conditions, for example you delimiter characters cannot easily be used in simple SQL queries and PreparedStatements statements overcome this problem by separating the parameter data from the query.

PreparedStatements are setup in a very similar way to the simple SQL statements seen above..

The database.xml file

  1. <Connection id="Mortgage" driver="com.mysql.jdbc.Driver"
  2. url="jdbc:mysql://localhost/mortgage" user="mortuser" pwd=""/>
  3. <ResultSet id="InsertApplication" conn="Mortgage"
  4. sql="INSERT INTO application ( added_by, create_date, finance_amt,
  5. property_value )
  6. VALUES ( ?, ?, ?, ? )"/>
  7. <ResultSet id="GetApplication" conn="Mortgage"
  8. sql="SELECT application.application_id, added_by, finance_amt,
  9. property_value,
  10. applicant_id, title, firstname, surname, dob, bankname,
  11. bankaccount_no, bank_nsc
  12. FROM application, applicant
  13. WHERE application.application_id = ? AND
  14. applicant.application_id = application.application_id"/>

The PreparedStatements are declared in the standard way with question marks indicating the data which will be expected for the query. Within you Java code the . Calling a PreparedStatement

  1. DatabaseTableModel dtmRes = null;
  2. String params[] = { "testuser", "22/11/2005", "80000", "140000" };
  3.  
  4. DatabaseTableModel dtm = (DatabaseTableModel)ProjectManager.getModel().get( "InsertApplication" );
  5. dtm.setParams( params );
  6.  
  7. dtmRes = (DatabaseTableModel)dtm.get();
  8. dtmRes.setDistinct( true );
  9. dtmRes.setDirty( true );
  10. dtmRes.retrieve();
  11. dtmRes.first();

By calling the setParams function in the DatabaseTableModel it is aware that it needs to execute a PreparedStatement which needs to be populated using the params array.

Using connections directly

The DataConnection class exposes some access to the underlying connection and JDBC statement objects, but these must be used with care. All the usual JDBC constraints hold, and for each connection there can only be one statement open at a time. The DataConnection object expects to manage a single statement at a time and it will close any open statements before attempting to create a new statement. For each method such as createStatement or createPreparedStatement there is an equivalent closeStatement or closePreparedStatement and the matching method must be called to properly manage the JDBC connection. Failure to honour this contract may result in unexpected results and leaking resources.

Adding data

The DatabaseTable class and its derivative ( CachedDatabaseTable ) and the DatabaseTableModel classes include limited functionality to write to a database. The classes include setValue methods that write individual field values to a database. In some circumstances where you need to perform limited database writes this may be sufficient but in other cases you would be better served by creating PreparedStatements where you can write multiple field values in one statement.

Furthermore as has already been mentioned there are certain restrictions on the types of values that can be written to a database as part of the query and using a PreparedStatement can also help work around these issues.

The API also distinguishes between a writable database and one that is read-only as in the case of the setSqlStatement used above. Please refer to the API documentation for further details.

Named connections

Some applications rely on access to more than one database and the configuration files shown above have only listed a single database, the default database. However the configuration file may refer to additional databases by other names and specifying other parameters:

Referencing multiple databases

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Datasets>
  3. <Connection id="default" driver="org.hsqldb.jdbcDriver"
  4. url="jdbc:hsqldb:testdb" user="sa" pwd=""/>
  5. <Connection id="crm" driver="org.hsqldb.jdbcDriver"
  6. url="jdbc:hsqldb:crmdb" user="sa" pwd=""/>
  7. <ResultSet id="Voltages" conn="default" from="CS_VOLTAGES"/>
  8. <ResultSet id="Customers" conn="crm"
  9. sql="SELECT FirstName, SecondName, Telephone FROM CUSTOMERS"/>
  10. </Datasets>

Configuring connections

In certain circumstances you may not wish to include the connection details in the configuration file or you may wish to specify them dynamically. Aria includes a NamedConnectionManager facility to do this.

Resetting the database connection

  1. NamedConnectionManager connMgr = ( NamedConnectionManager )NamedConnectionManager.getInstance();
  2. connMgr = (NamedConnectionManager)connMgr.reset( connName,
  3. dbDriverStr,
  4. dbUrlStr,
  5. userName,
  6. passwordStr );

The default connection names is " default " and resetting this connection will affect all subsequent use of this connection. Other connections can be added with the addConnection method which takes the same familiar JDBC connection parameters. Many of the database access functions include a reference for the connection name and in this way you should be able to control database access. Note that if you need to configure the connection at startup you will need to reset the connection as soon as possible, possibly even subclassing the applet class. If you leave the framework to load the database configuration files then you will need to ensure that there is an entry for the Connection in the datasets file even if the user name and password are blank.

Debugging

When working with database it is often useful to use the debug versions of the Aria libraries. These libraries output the SQL statements as they are passed to the JDBC database driver. Once you have the SQL you can work with the database's tools directly to tweak and adjust the SQL till you get the expected results.

Many databases also have advanced tools for profiling and optimization and you can use these to build the queries and some will also allow you to log the queries being made against the database.

If you do not have such tools there are a number of commercial and open source tools for such purposes for example:

Some free or open source database tools
ToolURL
Squirrel SQLhttp://squirrel-sql.sourceforge.net/
DB Wrenchhttp://www.dbwrench.com/
Aqua Data Studiohttp://www.aquafold.com/

Databases

Although we do not intend recommending any particular database there is a certain category of database that is of particular use when working with Aria applications, that is pure Java databases. Aria applications can often run off-line or in standalone mode.

Standalone application frequently need off-line storage and databases of their own. The pure Java databases make this particularly easy and remove much of the need for special installation. The HSQLDB is one such database and is of particular interest being of small footprint and offers good performance for smaller databases. The database even includes an in-memory mode that makes it easy to include in an application distribute.

The HSQLDB has also been chosen as the database for OpenOffice 2.0. The office suite includes some nice database tools and the combination of the two makes for some interesting design, analysis and reporting opportunities.

One of the great strengths of Java is the vast array of databases that can be accessed via JDBC. Sometimes however you may not have a pure Java JDBC database driver available and in such cases you can fall back on the ODBC driver via the JDBC-ODBC bridge. The drawback of this approach is that you have to configure a DSN and that requires manual intervention on native code.

Another class of database that is of interest when programming Aria applications is the lightweight database. Many of the major database vendors offer such databases and they include features like replication and synchronization that is of great interest for mobile applications. Aria includes some features along these lines but this manual does not yet document such features (please contact Formaria directly for further information).

Transferring data

Aria includes a utility to help transfer data between JDBC databases. The utility can be found in the org.formaria.util.database.DatbaseTransferManager class.

For example when transferring data to a local instance of the HyperSQL database the parameters transfer manager prompts for the source database connection string and then the destination database connection string e.g:

where jdbc:hsqldb:file:/C:/CVS/RS3/resources/rs3db is the name of the HSQLDB instance.

Database synchronization

Frequently it is not enough to occasionally update a database as this might leave the local database in an out of date state. Furthermore if data is added or modified locally then this data may need to be saved back to a central server and propogated to other application users. Synchronizing the databases is one solution to this problem. Using a synchronization mechanism data from one database instance can be replicated to another in a bi-directional fashion so that both databases store the same data.

A number of commercial database replication facilities are available and they give you a range of choices in terms of platforms and in terms of capabilities, some for example support embedded databases and hetrogeneous database replication.

Aria has a built-in database replication facility and while it may not be as complete as some of the commercial systems it is already integrated with the Aria data model and requries little configuration. The Aria replication service may therefore be a good choice for some applications, but of course this should be evaluated in terms of the database technologies, scalability requirements and other system considerations (your Aria application may not be the sole user of a particular database).

Aria's replication service can be configured by adding a replicationService service

Client side replication service

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Services>
  3. <service id="replicationService" route="replicationService">
  4. <arg name="table" type="string" mandatory="true"/>
  5. <arg name="key" type="string"/>
  6. </service>
  7. </Services>

and a replicationService route to your application that employs the XReplicationService as a endpoint for the replication route.

Replication route on the client side

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Routes>
  3. <route id="replicationService">
  4. <layer class="org.formaria.service.ZipEncoderService"/>
  5. <layer class="org.formaria.service.HttpClientServiceProxy"
  6. url="http://localhost:8084/MyReplicator/ReplicationServlet" urlEncode="true"/>
  7. </route>
  8. </Routes>

The URL references the location of the routing servlet. On the server side the replication service also needs to be named: Server side replication service configuration

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Services>
  3. <service id="replicationService" route="replicationService">
  4. <arg name="table" type="string" mandatory="true"/>
  5. <arg name="key" type="string"/>
  6. </service>
  7. <service id="getDDL" route="replicationService">
  8. <arg name="table" type="string" mandatory="true"/>
  9. <arg name="key" type="string"/>
  10. </service>
  11. <service id="getData" route="replicationService">
  12. <arg name="table" type="string" mandatory="true"/>
  13. </service>
  14. <service id="postUpdates" route="replicationService">
  15. <arg name="table" type="string" mandatory="true"/>
  16. <arg name="data" type="string" mandatory="true"/>
  17. </service>
  18. <service id="postInserts" route="replicationService">
  19. <arg name="table" type="string" mandatory="true"/>
  20. <arg name="data" type="string" mandatory="true"/>
  21. </service>
  22. </Services>

The service configuration names several methods of the replication services for the various create, update, insert and delete operations supported. Ultimately these methods are implemented by the services that comprise the server side route:

Server side replication route

  1. <Routes>
  2. <route id="replicationService">
  3. <layer class="org.formaria.service.ReplicationService"/>
  4. <layer class="org.formaria.service.ZipDecoderService"/>
  5. </route>
  6. </Routes>

The only other requirement for this service is that the central database contains a table for use by the replication service. The DDL for this table is shown below.

TODO insert the xSysServelTimestamps table DDL

With the replication service configured in this way it will attempt to connect to the relication servlet and update the databases whenever a database access/query is made on the client side. Little by way of other configuration is required.

HSQLDB, Apache Derby/Cloudscape/JavaDB and Microsoft SqlServer are supported. SQL for other databases can be configured to work with the replication service.

The sample configurations above used a Zip encoding/decoding service to compress data for transmission over the wire. While this service is not strictly necessary it may be desireable if significant amounts of data are being transferred, for example if a complete snapshot of the database is being downloaded.

HTTP is not the only communications protocol that can be used and other communications protocols and channels can be used in place of the HTTP service, however it will be important to check the timing and delivery considerations of such protocols (for example a JMS service may deliver the updates out of order and on some databases this could give rise to consistency problems).

POJOs

The modelling of tables has been refactored so that more generic implementations can be provided, the first of which is construction of tables from ArrayLists and Vectors . The new class CollectionTableModel supports the new TableModel , RowModel and FieldModel setup.

For example

Setting up a collection

  1. public class MyTable extends Page
  2. {
  3. ...
  4.  
  5. public MyTable()
  6. {
  7. String[] names =
  8. { "ID", "Title", "Author" };
  9.  
  10. String[][] rawData = {
  11. { "0", "Moby Dick", "Herman Melville" },
  12. { "1", "The Hunchback of Notre Dame", "Victor Hugo" },
  13. { "2", "The Idiot", "Fyodor Dostoevsky" },
  14. { "3", "Ulysses", "James Joyce" },
  15. { "4", "Thus Spake Zarathustra", "Friedrich Nietzsche" },
  16. { "5", "Bleak House", "Charles Dickens" },
  17. { "6", "Mansfield Park", "Jane Austen" },
  18. { "7", "Alice's Adventures in Wonderland", "Lewis Carroll" },
  19. { "8", "The Republic", "Plato" },
  20. { "9", "Kidnapped", "Robert Louis Stevenson" },
  21. { "10", "On the Duty of Civil Disobedience", "Henry David Thoreau" },
  22. { "11", "The Jungle Book", "Rudyard Kipling" },
  23. { "12", "The Picture of Dorian Gray", "Oscar Wilde" },
  24. { "13", "The Rime of the Ancient Mariner", "Samuel Taylor Coleridge" },
  25. { "14", "Catcher in the Rye", "J. D. Salinger" },
  26. { "15", "The Glass Bead Game", "Herman Hesse" },
  27. };
  28.  
  29. ArrayList fieldNames = new ArrayList();
  30. for ( String n : names )
  31. fieldNames.add( n );
  32.  
  33. ArrayList data = new ArrayList();
  34. for ( String[] row : rawData ) {
  35. ArrayList rowList = new ArrayList();
  36. for ( String field : row ) {
  37. rowList.add( field );
  38. }
  39. data.add( rowList );
  40. }
  41.  
  42. CollectionTableModel ctm = new CollectionTableModel( project, "CollectionTable", fieldNames, data );
  43. rootModel.append( ctm );
  44. }
  45. ...
  46. }

combines with the page XML below to bind the collection to the 'collectTable' table component.

Binding components to a Java collection

  1. <Page class="aria.projects.sqltables.MyTable" layout="border">
  2. <Components>
  3. <Label name="title" content="XTable Demo" alignment="center" constraint="north" style="Heading"/>
  4. <TabPanel constraint="center" >
  5. ...
  6.  
  7. <ScrollPane title="Collections">
  8. <Table2 name="collectionTable" title="collection" interactive="true" headingStyle="TableHeading"
  9. style="TableData" selectionStyle="TableSelection" horizontal_scrollbar="as needed"
  10. updateModel="true"/>
  11. </ScrollPane>
  12. </TabPanel>
  13. ...
  14.  
  15. </Components>
  16. <Events>
  17. ...
  18.  
  19. </Events>
  20. <Data>
  21. ...
  22.  
  23. <Bind target="collectionTable" source="CollectionTable" output="tables/ct" />
  24. </Data>
  25. </Page>

The POJO model may be home grown or generated with tools such as JDO or Hibernate. Most of the configuration takes place automatically via reflection and little needs to be done other than pointing Aria at the root of the POJO model.

The following steps show how to build a two-tier Aria application which uses Hibernate POJOs as its data model.

Setting the data source

The XML entry, describing the data source object tasked with loading and adapting POJOs to the Aria model, needs to be set in the file pointed to by the ModelData startup property (datasets.xml by default). The aformentioned XML entry can look like this:

Setting POJO data source

  1. <Datasources>
  2. ...
  3. <DataSource filename="pojos.xml" name="Pojos"
  4. type="org.formaria.data.pojo.PersistentPojoDataSource"/>
  5. ...
  6. <Datasources>

In the listing above the filename attribute points to a file containing model data POJOs configuration.

Setting POJO model root node

The data model POJOs configuration file must include the <root> XML entry pointing to a class whose instance will be attached to the Aria data model and will serve as a "POJOs repository". The listing below shows the sample entry. Setting the root POJO

  1. <Pojos>
  2. ...
  3.  
  4. <root id="pojo" class="aria.ce.model.PojoBase">
  5. <param class="String" value="pojo base object"/>
  6. </root>
  7. ...
  8. </Pojos>

The class attribute points to a class which will be instantiated and attached to the Aria data model via DataModel object, the constructor attributes are defined in the <param> entries. The id attribute of the <root> entry defines the data model path at which the DataModel object wrapping the root POJO will be located.

Accessing POJO model nodes

The general rule of accessing POJOs in the data model is: if the DataModel node wrapping a given POJO is located at the parentPath path and defines property named p1, (via accessors: getP1(), setP1(...)) then the DataModel node wrapping the value of this property is located at the parentPath/p1 path. The actual value of the property can be obtained by invoking the "get()" method on the encapsulating DataModel node. The source of the root class being used in the example is:

The source of the root POJO

  1. package aria.ce.model;
  2.  
  3. import auction.dao.DAOFactory;
  4.  
  5. public class PojoBase
  6. {
  7. private String pojoBaseName;
  8.  
  9. public PojoBase( String name )
  10. {
  11. pojoBaseName = name;
  12. }
  13.  
  14. /**
  15.   * Gets the hibernate data access object factory, all DAOs
  16.   * being used in the application can be otained from it.
  17.   */
  18. public Object getHibernateDAOFactory()
  19. {
  20. return DAOFactory.instance( DAOFactory.HIBERNATE );
  21. }
  22.  
  23. public String getId()
  24. {
  25. return pojoBaseName;
  26. }
  27. }

As can be seen the class defines a hibernateDAOFactory property, its value will be located at the pojo/hibernateDAOFactory path, or to be more acurrate, the wrapping model node will be located at the mentioned path.

Overriding POJO properties

POJO properties can be overriden, via the data model POJO's configuration file (pojos.xml in the example). The listing below shows the sample overloading.

Overriding POJO properties

  1. <Pojos>
  2. ...
  3. <pojo id="categoryDAO" class="auction.dao.hibernate.CategoryDAOHibernate">
  4. <property id="rootCategories" getter="all(Boolean:true)"/>
  5. <property id="allCategories" getter="all()"/>
  6. </pojo>
  7. ...
  8. </Pojos>

Each entry defines how individual class is overriden. While the id attribute is not being used at the moment, the class attribute specifies a class whose properties are customized. entries define accessors that should be used to get/set property value specified by the id attribute. With the definition as in the listing above, evaluating the model path categoryDAO/rootCategories would result in invocation of the getAll(true) method on the CategoryDAOHibernate object stored at the categoryDAO path.

Transient properties

In the data model's POJO configuration file a special type of properties, named transient properties, can be defined. The listing below shows the sample definition.

Transient properties

  1. <Pojos>
  2. ...
  3. <pojo id="categoryDAO" class="auction.dao.hibernate.CategoryDAOHibernate">
  4. <transient_property id="selectedCategory" class="auction.model.Category"/>
  5. </pojo>
  6. ...
  7. </Pojos>

The entries define an additional accessor-less properties which serve as a place to store POJOs in the data model. Invoking the set(arg)/get() method on a model node wrapping such a property will simply store/return the POJO, neither a getter nor setter will be invoked on the parent model node. The reason for this is to provide the ability to declaratively define POJO properties, that may be useful in an application as a part of the data model, but are not connected with a business logic which the POJO's native implementation is intended to define. For example, an application may want to display the details of the selected Category object when the selection is made in the list of all categories. In such a scenario, the model node associated with the selected category would be defined as a transient property. The listing below is an example of this.

Transient property binding in a page xml source

  1. <Page class="mypackage.EditCategories">
  2.  
  3. <Components>
  4. <List name="allCategoriesList" ..../>
  5. <Edit name="selectedCategoryName" ... />
  6. <List name="subCategoriesList".../>
  7. <Components/>
  8.  
  9. <Data>
  10. <Bind target="allCategoriesList" source="categoryDAO/allCategories" attrib="name"/>
  11. <Bind target="selectedCategoryName source="categoryDAO/selectedCategory/name"/>
  12. <Bind target="subCategoriesList"
  13. source="categoryDAO/selectedCategory/childCategories" attrib="name"/>
  14. <Data/>
  15.  
  16. <Events>
  17. <Event method="categoryListSelectionChanged"
  18. target="allCategoriesList" type="ListSelectionHandler"/>
  19. </Events>
  20. <Page/>

List selection handler updating components bound to the transient property

  1. public EditCategories extends Page
  2. {
  3. ...
  4. // invoked whenever the selection on allCategoriesList is changed
  5. public categoryListSelectionChanged()
  6. {
  7. // get selected category model from the list
  8. Category category = getSelectedCategory( allCategoriesList );
  9. // put selected category to the transient property model
  10. rootModel.get( "categoryDAO/selectedCategory).set( category );
  11. // update the UI (show the name and subcategories of the selected category)
  12. updateBoundComponentValues();
  13. }
  14. ...
  15. }

Setting ORM specific configuration

It is possible to use ORM (i.e Hibernate, JPA) POJOs as a data model. This requires some addiction configuration. Following the DAO pattern, the first thing that has to be done is to create a set of Data Access Object that will serve as a layer between the POJOs and the persistent store. Each of these objects has to be declared in the configuration file (pojo.xml). The sample configuration can look like this..

Declaring DAO objects

  1. <Pojos>
  2. ...
  3. <pojo id="userDAO" class="auction.dao.hibernate.UserDAOHibernate" dao="true"/>
  4. <pojo id="categoryDAO" class="auction.dao.hibernate.CategoryDAOHibernate" dao="true"/>
  5. ...
  6. </Pojos>

The declaration from the listing above requires that before accessing (via accessor methods) any of the properties belonging to either UserDAOHibernate or CategoryDAOHibernate instances, the session "bound" to this property should be opened. This is being handled automatically via the Persistence Controller whose implementation will be described later.

The next thing that needs to be added to the POJO model configuration file is the definition of lazily initialized POJO properties which means properties whose values are being retrieved from the persistent store "on demand". This kind of properties are supported by either Native Hibernate and JPA ORMs. The listing below shows the sample configuration.

Lazily initialized properties configuration

  1. <Pojos>
  2. ...
  3. <pojo id="book" class="auction.model.Book">
  4. <property id="categories" lazy="true"/>
  5. <property id="bids" lazy="true"/>
  6. </pojo>
  7. ...
  8. </Pojos>

This configuration specifies that two things have to be done before accessing any of the listed properties (before invoking getter methods). First: the session bound to the given property has to be opened, and second: the parent object (in this case: an instance of the Book class) must be in the "persistent" state. All this is handled by data model and persistent controller which transparently begin and end sessions (database transactions) when the UI is updated, bindings are evaluated, etc.

When the Native Hibernate serves as an ORM framework this configuration is retrieved from the mapping files (specified in the "hibernate.cfg.xml" file), so there is no need to provide it explicitly.

Setting up the Persistence Controller

The next thing is to create and setup the Persistent Controller object which must implement the XPersistenceController interface including methods tasked with beginning/ending sessions, saving/removing entity objects, etc. This can be done with the Native Hibernate as well as with the JPA ORMs. The sample implementataion of one of the interface's methods "persist(Object o)" is shown below.

the Native Hibernate implementation of the "persist" method

  1. import org.hibernate.*;
  2.  
  3. public class HibernatePersistenceController
  4. {
  5. ...
  6.  
  7. /**
  8.   * Makes the specified object persistent.
  9.   * @param object the entity to be saved.
  10.   */
  11. public void persist( Object object )
  12. {
  13. Session session = HibernateUtil.getSessionFactory().
  14. getCurrentSession();
  15. session.saveOrUpdate( object );
  16. }
  17.  
  18. ...
  19. }

the JPA implementation of the "persist" method

  1. import javax.persistence.*;
  2.  
  3. public class JPAPersistenceController
  4. {
  5. ...
  6. private EntityManager entityManager;
  7.  
  8. /**
  9.   * Sets the entity manager that should be used
  10.   * by this controller.
  11.   * @param em the entity manager to be used
  12.   */
  13. public void setEntityManager( EntityManager em )
  14. {
  15. entityManager = em;
  16. }
  17.  
  18. /**
  19.   * Makes the specified object persistent.
  20.   * @param object the entity to be saved.
  21.   */
  22. public void persist( Object object )
  23. {
  24. entityManager.merge( object );
  25. }
  26.  
  27. ...
  28. }

The last thing is to add an entry pointing to a class that implements XPersistenceController to the POJO model configuration file, as shown in the listing below.

setting up the Persistence Controller

  1. <Pojos>
  2. ...
  3. <context class="aria.ce.HibernatePersistenceController"/>
  4. ...
  5. </Pojos>

Visualization

The data model which is built into Aria is a very powerful and versatile data storage mechanism, however, it can become quite difficult to navigate its multi layered hierarchy from a debugger. This is where the visualizer comes into its own.

The data visualization built into Aria is meant to be used as part of your development, debugging and testing but is not meant to be deployed as part of the final release application.

Using the Visualizer within the Aria Editor

Start the Aria editor in NetBeans and click the Aria|Visualization|Show the data visualizer menu.

The visualizer will appear docked in the NetBeans IDE and will reflect the data which is contained in the files referenced from the datasets.xml file. As you reference parts of the model from the components will build itself and you can use the visualizer to simplify the binding of components

  • The left pane contains a tree which allows you to navigate all of the paths in the data model.
  • When you click a node in the tree its attributes are displayed in the structure table on the top right of the window. This table will always include the `value' and `id' attributes regardless of whether they contain data or not.
  • The refresh button below the model tree needs to be clicked whenever a change to the model has taken place if you want a true reflection of the current state of the data model.
  • The toolbars above the data model and attributes tables offer a number of extra options for manipulation of the model. Some of these options are also available on the node's popup context menu.
  • Visualizer tools

    Icon/Tool

    Purpose

    Refresh the model

    Adds a new node to the model

    Deletes the current node from the model

    Edit the model node

    Rename the model node

    Add a new attribute to the model node. This method does not prevent duplicates

    Delete the selected attribute from the model node. The standard attributes (id and value) cannot be deleted

    Copy to the model path to the clipboard. The full path of the selected node is placed on the clipboard. This value can then be pasted as text.

The right pane of the Visualizer contains two tabs: structure and visualizer.

The structure tab contains a table displaying the selected model's attributes and its values. The third runtime column indicates whether the attribute is also available in the runtime mode (some attributes are used only to configure the datasource in the Visualizer and thus are not visible in the runtime mode, the example of that is POJOs getter attribute).

The visualizer tab is used for rendering data model nodes in a way that is specific to the selected data type. For instance, with the database table node selected in the visualizer's tree, the tab will contain a JTable component reflecting the data from the selected table

The visualizer tab can be used also for setting some specific model nodes configuration. The example of that is a panel that allows the user to specify values of the POJOs property parameters (in other words: to specify parameters of the getters which define POJO properties).

If you wish you can pre-define your model in one of the dataset files then you can start binding your screen components more easily. For example you can define the following nodes in the model

Predifined model

  1. <datasets>
  2. <dataset id="${getCustomerID()}">
  3. <data value="" id="surname"/>
  4. <data value="" id="dob"/>
  5. <data value="" id="firstname"/>
  6. <data id="gender"/>
  7. <data value="" id="title"/>
  8. <data value="" id="address1"/>
  9. <data value="" id="address2"/>
  10. <data value="" id="address3"/>
  11. <data value="" id="postcode"/>

Now, open the customer details page and click on the customer surname edit field. Expand the data model in the visualizer and double click on the node ${getCustomerID()}/surname . You will see the data property for the component change to this value and any time you click on this component the visualizer will automatically select this node.

Setting up database table binding using the Visualizer

Setting up database table binding using the Data Visualizer

Step

Action

1

Open the runtime view from the window menu, expand the Databases node and establish a connection to the database containing a table which you want to bind. The available database tables will appear under the Tables node in the Runtime Tab.

2

Drag and drop the selected table from the runtime|database node to the Data node displayed in the Visualizer. The table will appear in the data model hierarchy and will configured and registered in the data model so it can can used in a standalone application.

3

Once the table is registered its data can be viewed under the Visualizer tab.

4

Drag and drop the table node from the Visualizer to a component that you wish to bind it to.

5

As the table node is dropped you will be asked to configure some properties of the bindings in the popup dialog (in most cases the default values will suffice).

6

Once the dialog is closed (by pressing OK button) the binding will be set and configured. The Data property of the component will get the specified value.

Debugging with the Visualizer

The visualizer makes it easier to debug your Aria applications as it provides you with an up to date representation of the datamodel at any given time. When you start a debugging session for a Aria application from netBeans the visuaslizer will automatically update with the state of the model. It will start from the root of the project's model and create the entire model hierarchy.

Evaluated Attributes and Helpers

Evaluated attributes provide a powerful way of extending the scope and range of what Aria can do. Take for example a simple data binding where a single source node is mapped to say an edit field. On its own this binding is limited, but if we use an evaluated attribute then we can control what is displayed each time the binding is updated, and earlier (Callbacks.) we saw an example of this dynamic data binding.

Evaluated attributes offer a great deal of power and reinforce the idea of the Model-View-Controller architecture by providing a means to insert links between the various components without hard wiring of these links.

Evaluated attributes

First of all, to recap what an evaluated attribute is and how it works lets look at a very simple example.

Basic attribute evaluation

  1. <Button name="DecBtn" x="178" y="43" w="42" h="20" content="${getContent()}"/>

The code ${getContent()} is an expression that is evaluated at runtime each time the expression is encountered. For a page component declaration the expression is evaluated when the page is loaded.

Expressions can be used in other locations such as within the data model, the data bindings, the validations or the event bindings.

Arguments allowed in XML attribute method calls

The XML description of pages has been able to support method calls for the evaluation of attributes since version 1.0.3. These method calls now support method arguments. Arguments of int and String types can be included. For example:

An Evaluated Attribute

  1. <Bind target="connSizeSt" source="${getNodeValue(3)}" unique="true"/>

This example dynamically binds a value to the connSizeSt component. The implementing page should then contain the method

A Handler for an Evaluated Attribute

  1. public String getNodeValue( int NodeID )
  2. {
  3. ...
  4. }

The implementation of this method should return a String that can be used as the source attribute of the original XML Bind element.

The advantage of this method call is that the same page definition can now be reused and bound to different parts of the data model. A simple example of this might be where a popup dialog is used to show details of say a person in some business model. Such a person entity could be a manager, an employee or a visitor/guest and as such the data for each of these entities would exist in different parts of an organizational hierarchy.

Library expressions

In Aria 2.0 The attributes can also be defined in classes other than the current page or classes derived from Page . The syntax for such expressions is as follows:

Extended attribute declarations

Syntax

Behavior

${mypackage.MyClass.myMethod(args...)}

to invoke a static method

${mypackage.MyClass[].myMethod(args...)}

to create a new instance of the class on each evaluation

${mypackage.MyClass[referenceName].myMethod(args...)}

for a named object instance

${myMethod[referenceName](args...)}

for a method contained within the invoking page

${[referenceName].myMethod(args...)}

for a method contained within the class instance referred to by the reference name.

${this[referenceName].componentMethod(args...)}

for a method contained within the component referred to by the reference name. Since 2.0.7

${project.projectMethod(args...)}

for a method contained within the current project. Since 2.0.7

${this.myMethod(args...)}

for a method contained within the enclosing page class. This is provided for compatibility with the basic method invocation mechansim. Since 2.0.7

${a/b/c.myMethod(args...)}

for a method contained within a POJO attached to the Aria data model. The POJO at the model path a/b/c containing the method myMethod will be invoked. since 4.0

where mypackage is the name of the Java package containing the class MyClass . The value of referenceName is a user defined value that identifies the instance of the class. The application instantiates an instance of the class when the expression is first encountered and thereafter maintains the instance with each subsequent call retrieving the same instance of the class. As in early versions, the method call can also contain zero or more arguments. For an example of this please see Evaluated attributes.. Library expressions can be used in the specification of component attributes, events and for data bindings.

Expression evaluators

The expressions are evaluated by an ExpressionEvaluator and each page has by default its own instance of the default expression evaluator. (The default evaluator delegates storage of the referenced classes to the project). However, the page allows this evaluator to be replaced and a different evaluator can be inserted with a call to the AriaBuilder.setAttributeEvaluator method. This replaceable evaluator allows a route to include other expression evaluators such as interpreters. Therefore if the capability of the built in evaluate is insufficient you can replace or extend the default evaluator. As an example a prototype evaluator for the Groovy language has been created.

Using evaluated attributes

OK, so we have seen how the attributes of an XML file can in fact be callbacks to methods in your page class or in some other class. What does this mean for the application?

Essentially this means that the model is dynamic, it can be adapted to meet the changing needs of your application as a session progresses. You are not restricted to the setup encoded in the XML at start-up.

The dynamic model also means the data structure specified in the XML (or in your code for that matter) can be mapped from one instance to another via the evaluated attributes and callbacks. We have already seen how this could be use with something as simple as an address form.

Evaluated attributes also make it possible to apply more advanced techniques like filtering data and providing access control. Once you get to grips with the basic functionality you should find the use of evaluated attributes a very powerful mechanism.

From an achitectural point of view the use of evaluated expressions means that you can create a user interface with XML with very little dependancy upon the Java code. The page does not need to be derived from a custom class as you logic can be embedded in some other hierarchy.

The use of library functions also promotes reuse as the code can be freed of some of the page specific constraints. Late binding also opens up further possibilities for dynamically matching code to the user interface and providing different levels of functionality or strategies for different situations or even for different users.

The on-line mortgage tutorial uses library methods to implement its page navigation. In the pre 2.0 versions the navigation required a special page to implement this behavior and therefore the navigation page became tied to the navigation class which was undesirable if you wanted to use features of another page class (assuming that the navigation is only a small part of the page's functionality). With the new library functions the navigation features can be added as mix-ins with very few special requrements.

The added flexibility of this arrangement should make it possible to build reusable functionality that can be dropped in to various points of your application.

Escape sequence for path attribute values

When searching for a model node it is possible to specify an attribute value to distinguish between nodes with the same parents. For example:

A Model Path and Attribute

  1. model.get( "products/software/vendor@name=Formaria" );

This searches for a node where the vendor has a name attribute of Formaria . Sometimes, for instance when handling code numbers or article numbers, the attribute may include forward slashes. These slashes can cause problems when trying to identify various parts of the path and therefore an escape sequence has been added to allow attributes with slashes to be used in searches. Such a search would appear as:

A Model Path and a Specified Attribute

  1. model.get( "products/tyres/vendor@name=michelin/size@value=[R14/500]" );

In the above example the search is for a tyre from the michelin vendor and with a size of R14/500 .

Handling evaluation exceptions

An exception handler has been added to the attribute evaluator. The handler gets called in case of an exception and can override the result returned by the attribute evaluator. The evaluator may be of use in case, for example, an evaluation depends on a list selection and where that list may not have a selected values - the list would otherwise return a value such as null or -1 to indicate the error and this is probably not a valid value for the evaluated attribute. Say a path of a/b/${c}/d/e is enetered and ${c} depends on say a list selection and that list is not fully initialized.

TODO add example code

An example

To illustrate the use of evaluated attributes a dialog with dynamically loaded text will be displayed. The text to be loaded comes from an extrenal HTML file as it is too large to be embedded in the page XML or the language resource bundles.

First of all lets look at the XML:

Setting the text via a callback function

  1. <Page class="org.formaria.demo.About" padding="10">
  2. <Components>
  3. <Image x="526" y="0" w="74" h="30" imageName="danfoss.gif" />
  4. <ScrollPane x="10" y="50" w="600" h="250" style="base/info" border="0"
  5. vertical_scrollbar="as needed" horizontal_scrollbar="never">
  6. <Label x="10" y="50" w="600" h="400" content="${getExtraText(terms)}"
  7. style="base/info"/>
  8. </ScrollPane>
  9. <ImageButton name="closeBtn" x="510" y="310" w="100" h="28"
  10. style="GraphicButton" text="OK"
  11. imageName="button.png" rollover="button_over.png"
  12. pressed="button_pressed.png"/>
  13. </Components>
  14. <Events>
  15. <Event method="closeDlg" target="closeBtn" type="ActionHandler"/>
  16. </Events>
  17. </Page>

Then in the about class the referenced method is implemented:

Implementingthe evaluated method

  1. public class About extends Dialog
  2. {
  3. public About()
  4. {
  5. }
  6.  
  7. /**
  8.   * Get the text for a popup from an extra file
  9.   * @param fileName the argument from the XML file
  10.   */
  11. public String getExtraText( String fileName )
  12. {
  13. // Get the default language from the preferences
  14. Preferences rsPrefs = Preferences.userNodeForPackage( Welcome.class );
  15. String defLangCode = rsPrefs.get( "defaultLanguage", "en" );
  16.  
  17. // Build the path to the file - use a separate sub folder for each language
  18. String filePath = defLangCode + File.separator + fileName + ".html";
  19. try {
  20. // Open and read the file
  21. Reader r = project.getBufferedReader( filePath );
  22.  
  23. StringBuffer text = new StringBuffer();
  24. int BUF_LEN = 1024;
  25. int len = -1;
  26. char buffer[] = new char[ BUF_LEN ];
  27. while (( len = r.read( buffer, 0, BUF_LEN )) >= 0 )
  28. text.append( buffer, 0, BUF_LEN );
  29.  
  30. // Now return the text
  31. return text.toString();
  32. }
  33. catch ( Exception e )
  34. {
  35. e.printStackTrace();
  36. }
  37.  
  38. // The resource was not loaded for some reason
  39. return translate( "Missing resource" );
  40. }
  41. }

With just a few lines of reusable code it is now possible to load content from a variety of sources much in the manner of templates. In fact, the above method has little need to reside in a page class and could just as easily reside in a library class further extending the resuability of the evaluated attribute.

The results of this coding can be seen below:

Scripting

Scripting should be a natural complement to Aria. With most of the plumbing taken care of by the framework there is normally very little Java code, but even so having the extra capabilities of a language such as Groovy should be useful in simplifying application development in certain domains.

Being able to modify and execute quickly without reloading the application could be a real time saving during development.

Aria has had scripting support for some time, but it was limited to the valuation of attributes and scripts invoked via the attribute evaluation. With the new scripting support the complete page functionality can be implemented by a script and not only is this support more extensive but it is also simpler as the script methods do not need to be explicitly identified as script methods within the page XML. The new scripting support extends the script engine approach used in Aria 3 and it should not be a huge leap to provide JSR 223 support.

Adding Groovy Support

To use the new support the appropriate builder must first be added to the project..

Setting up the Groovy Builder

  1. NumBuilderClasses=1</P>
  2. <P>
  3. BuilderClass0=org.formaria.scripts.groovy.GroovyBuilder

Once the builder has been loaded Aria will search fro .groovy files to implement the page class, just as it would search for a .class file if the page was implemented as Java. The page builder will also check if the script has been modified since the last load, and if it has the script will be reparsed. Reloading the script means that you can easily change the behavior of a running application. Thus the page can be declared as normal:

Declaring a page to use a Groovy script

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page class="aria.groovy.Welcome.groovy">
  3. <Components>
  4. <Button name="nextBtn" x="100" y="100" w="100" h="20" content="Next"/>
  5. </Components>
  6. <Events>
  7. <Event target="nextBtn" method="next" type="ActionHandler"/>
  8. </Events>
  9. </Page>

note that the class attribute of the page now points to a Groovy file. The Groovy is then as follows:

A Groovy page script

  1. package aria.groovy;
  2.  
  3. import org.formaria.aria.Page;
  4.  
  5. public class Welcome
  6. {
  7. Page page;
  8.  
  9. public Welcome()
  10. {
  11. }
  12.  
  13. public void pageCreated()
  14. {
  15. println "pageCreated invoked"
  16. }
  17.  
  18. public void next()
  19. {
  20. println "next invoked once again..."
  21. page.showPage( "MiddlePage" );
  22. }
  23. }

The project also needs to be modified to add the Groovy jar files - choose those jars you need or choose the groovy-all jar.

Component Factories

Working with components

At the heart of Aria is the component factory. The built-in component factory constructs the Aria components and comes in two flavours; one for Swing and one for AWT. Using component factories hides much of the detail of building individual components and makes it possible to change the implementation without having to change all the client code.

Aria allows component factories to be added and hence it can accommodate a wide range of components apart from the built-in set. Aria also includes a facility to register new components via an XML registry and even an editor for this registry

Choosing how to install components

Of the two methods of installing components, the XML registry is by far the simpler as you can simple use the interactive registry editor and its built-in component factory without any custom coding. The alternative is to build a custom component factory and directly instantiate the components.

While creating a component factory may be more work it also gives more control over the process of creating and initializing the components. Using a component factory you can achieve things that are not normally possible with the XML registered components, for example Aria's repeat element is implement via a special component factory.

Using the registry editor

The Integrated Component Registry Editor is a new feature in Aria 2.0

The component registry editor can be accessed by clicking the `*' button in the component palette toolbar. Almost all of Aria's non-core components are added via this registry so you can see how the properties of the indvidual components are configured.

The editor assumes that the components are held in Jar files and are represented as Java Beans. Upon opening the editor you may add or delete Jar libraries.

The editor then scans the Jar for suitable components and displays a hierarchical view of the available components. It is important to note that the editor does not automatically include any of the components or any of the component properties. It is up to you to tell the editor what to use.

The component properties are shown as an individual component is selected. You may rename the components so as to give it a tag that is suitable for use in an XML file. Then you can choose which methods to use as properties of an individual tag. Normally only the setter methods are used in the XML but you may also use the getter methods.

The methods may also be configured to be visible in various modes:

  1. Component configuration modes

Mode

Usage

Novice

A minimal set of properties for new users

Normal

A set of properties for normal everyday use

Expert

A fuller set of properties for detail component configuration.

The idea of these modes is that reducing the number of visible properties makes it easier to locate commonly used properties. Fewer options may also make it easier for a novice to make use of the component.

Once the component and its properties have been chosen the editor generates a new components.xml file for the application. The component palette is also updated and the components can be used in the application.

The registry file for each project also acts as an overlay for the built-in registry files. Both Aria Editor and Aria include registry files for their own components. These registry files are loaded as the first page is loaded after startup of the project. Each project has its own configuration file so that its configuration is independant of other projects even though it loads data from the shared sources of built-in registry files.

Aria itself uses the registry for loading component Jar files and therefore an individual application may use components registered via several configuration files. Aria and Aria Editor each register components this way and your application itself may add another, so an application will typically have several component registration files. However, for the most part you need not worry about these details as they are all catered for by the built-in regsitry editor.

The registry editor can be used in one of two modes. The first - a simple mode, is used when the editor is opened normally. In this mode only the methods with types that Aria and Aria can construct directly from XML are allowed.

In the advanced mode, which can be accessed by holding down the CTRL key while opening the editor, all the methods are shown. In the advanced mode it is left to the user to ensure that the property values are correctly set. Normally this is ok if coding via Java, but it is difficult to achieve when using XML to define the pages.

The registration file generated by the registration editor is saved under the user profile. As a project is edited the registration file held in the project's resources folder is updated as is the project's IDE settings. In addtion any Jars required by the components added during editing of the project are copied to the project's lib folder and references in the project's IDE settings.

Installing a component factory

The factory needs to be registered with the main class, this is done through the startup.properties file. The example adds one of the component factories shipped with Aria. This factory adds a number of Swing specific components. The registration factory is just another factory and following the above we add another startup property.

Including a Component Factory

  1. NumComponentFactories=1
  2. ComponentFactory0=org.formaria.swing.SwingComponentFactory

Setup the registration factory

First the factory needs to be registered with the main class, this is done through the startup.properties file.

Adding the XRegisteredComponentFactory

  1. <code>
  2. NumComponentFactories=2 </code>
  3.  
  4. <code>
  5. ComponentFactory0=org.formaria.swing.SwingComponentFactory</code>
  6.  
  7. <code>
  8. ComponentFactory1=org.formaria.registry.RegisteredComponentFactory</code>

Then, as this class is loaded it attempts to read the XML file detailing the components, this file is also named in the startup.properties file with the "ComponentRegistry" parameters e.g. Specifying the Component Registry

  1. ComponentRegistry=components.xml

The components file then details each component for example:

Using the New Components

  1. <components>
  2. <component name="MyComponent" class="myPackage.myClass">
  3. <property name="content" type="get" method="getText"/>
  4. <property name="content" type="set" method="setText"/>
  5. <Property type="both" name="autoStart">
  6. <Param type="boolean"/>
  7. </Property>
  8. </component>
  9. </components>

You can examine this file after the component registration editor has completed or you could code in manually. The registration can include all the methods available or just those of interest, and when editing an application it may be better to have fewer methods registered as the property sheet will then be more compact. Of course, the methods in the component classes can still be invoked so long as they follow the normal rules for Java beans in declaring getters and setters.

Using reflection

Reflection can be used to find all the component attributes if you don't wish to use the editor tools, or edit the registration XML manually

Taking the automatic registration one step further, it is possible to rely totally on reflection to find the accessor methods of a component. To register components in this way we just add the reflect="true" attribute. Thus, for example to register some Swing components we add the following entries to our project's components.xml file.

Registering Swing components via reflection.

  1. <Component name="JButton" class="javax.swing.JButton" reflect="true"/>
  2. <Component name="JCheckBox" class="javax.swing.JCheckBox" reflect="true"/>
  3. <Component name="JColorChooser " class="javax.swing.JColorChooser" reflect="true"/>
  4. <Component name="JComboBox" class="javax.swing.JComboBox" reflect="true"/>
  5. <Component name="JEditorPane" class="javax.swing.JEditorPane" reflect="true"/>
  6. <Component name="JFormattedTextField" class="javax.swing.JFormattedTextField" reflect="true"/>
  7. <Component name="JLabel" class="javax.swing.JLabel" reflect="true"/>
  8. <Component name="JList" class="javax.swing.JList" reflect="true"/>
  9. <Component name="JOptionPane" class="javax.swing.JOptionPane" reflect="true"/>
  10. <Component name="JProgressBar" class="javax.swing.JProgressBar" reflect="true"/>
  11. <Component name="JRadioButton" class="javax.swing.JRadioButton" reflect="true"/>
  12. <Component name="JSlider" class="javax.swing.JSlider" reflect="true"/>
  13. <Component name="JSpinner" class="javax.swing.JSpinner" reflect="true"/>
  14. <Component name="JSplitPane" class="javax.swing.JSplitPane" reflect="true"/>

In addition to describing the components in this way, the properties of a component can be set reflectively even if the (other) attributes have been specified explicitly. For example If there is no way of setting the attributes of a component directly, or through the XAttributedComponent interface, or through a ComponentAdapter then reflection will be used to set the properties. The methods signatures are parameter types are set using the JVM type signature syntax, for example

Using reflection to set a component property

  1. <List name="sourceList" x="10" y="11" w="305" h="251" dragEnabled="[Ztrue]"/>

where the dragEnabled attribute corresponds to a call to setDragEnabled( boolean state ) . See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/types.html for more details of the type specifications.

Using the new components

The components added by the new factory can be used immediately. Each component type should have unique name so when adding a new component all that is necessary is that you know this name. Then the normal Java or XML coding can be used to construct the component.

Jars added automatically to project

When adding components to a page from the Aria palette, jar files are imported to the project lib folder and added to the project properties. For example, the Spinner component utilises the AriaRuntimeCommon jar, this jar is thereby added to the projects lib folder and then added to the libraries in the project properties when the XSpinner component is first added to a Aria page in the project.

Anatomy of a ComponentFactory

Every component factory must implement the ComponentConstructor interface. This interface is relatively simple and is discussed below in the context of the source code for the SwingComponentFactory class.

A Component Factory Implementation

  1. 1 package org.formaria.swing;
  2. 2
  3. 3 import org.formaria.aria.ComponentConstructor;
  4. 4 import org.formaria.aria.ComponentFactory;
  5. 5 import java.awt.Component;
  6. 6 import java.util.Hashtable;
  7. 7
  8. 8 /**
  9.  9 * A factory for non-base Swing components such as Trees
  10.  10 * <p>Copyright: Copyright (c) Formaria Ltd., 2001-2004</p>
  11.  11 * <p>$Revision: 1.3 $ </p>
  12.  12 * License see license.txt
  13.  13 */
  14. 14 public class SwingComponentFactory implements XComponentConstructor
  15. 16 {
  16. 17 private String packageName = "org.formaria.swing";
  17. 18
  18. 18 public SwingComponentFactory()
  19. 20 {
  20. 21 // Register the extra binding factory
  21. 22 SwingDataBindingFactory.register();
  22. 23 }
  23. 24
  24. 25 /**
  25.  26 * A generic factory for adding XComponents. The component is constructed, positioned and
  26.  27 * added to the parent panel if one exists. The component is named with a counter value
  27.  28 * to uniquely identify the control.
  28.  29 * This factory does not use this method and all components must be added by name
  29.  30 * When a ScrollPane is addd it becomes the parent.
  30.  31 * @param cf the calling component factory
  31.  32 * @param type a constant identifying the type of component to be created
  32.  33 * @param content the component text/content
  33.  34 */
  34. 35 public Component constructComponent( XComponentFactory cf, int type, String content )
  35. 36 {
  36. 37 return null;
  37. 38 }
  38. 39
  39. 40 /**
  40.  41 * A generic factory for adding XComponents. The component is constructed, positioned and
  41.  42 * added to the parent panel if one exists. The component is named with a counter value
  42.  43 * to uniquely identify the control.
  43.  44 * @param cf the calling component factory
  44.  45 * @param type a name identifying the type of component to be created
  45.  46 * @param content the component text/content
  46.  47 */
  47. 48 public Component constructComponent( ComponentFactory cf, String type, String content )
  48. 49 {
  49. 50 Component comp = null;
  50. 51
  51. 52
  52. 53
  53. 54 if ( type.compareToIgnoreCase( "Tree" ) == 0 )
  54. 55 comp = new Tree();
  55. 56 else if ( type.compareToIgnoreCase( "Table2" ) == 0 )
  56. 57 comp = new Table2();
  57. 58
  58. 59 return comp;
  59. 60 }
  60. 61
  61. 62 /**
  62.  63 * A generic factory method for adding non component elements.
  63.  64 * @param cf the calling component factory
  64.  65 * @param type the object type
  65.  66 * @param name a name identifying the element to be created
  66.  67 * @param content the component text/content
  67.  68 * @param attribs the element attributes if any
  68.  69 */
  69. 70 public Object addElement( ComponentFactory cf, String type, String name, String content, Hashtable attribs )
  70. 71 {
  71. 72 return null;
  72. 73 }
  73. 74
  74. 75 /**
  75.  76 * Notify the component factories that some of their settings may have changed.
  76.  77 * This factory does not yet use any startup properties or parameters
  77.  78 */
  78. 79 public void update() {}
  79. 80
  80. 81 /**
  81.  82 * Set the package name for the factory's widgets.
  82.  83 */
  83. 84 public void setPackageName( String defPackage )
  84. 85 {
  85. 86 packageName = defPackage;
  86. 87 }
  87. 88 }
  88.  

: .

  1. Overview of the Component Factory Code

Lines 1-15

Register/Import individual components

Line 16

Names the package.

Lines 18-22

Construct a new instance of the factory and sets up the binding factory. The binding factory specifies the bindings that are used by default with the components in this factory. It is not necessary to register the binding types at this point but it is convenient to do so and this helps ensure that the bindings will be available immediately after component construction.

Lines 34-37

This is a deprecated API so no implementation is provided. The API was used in the first edition of Aria and proved difficult to extend and was therefore replaced by the API implemented in the next section.

Lines 47-59

Does the actual construction of the components. Essentially this factory is a switch on the component type name. This method is called from the component factory whenever its addComponent method is invoked. The factory iterates through the built-in component constructors and then the registered constructors till a component has been constructed for the named type. In this way it is important to name the component types uniquely and to order the factories in the correct order should there be a name conflict.

Lines 69-71

This method is not implemented but is required by the interface specification. The interface makes it possible to include non component information in a page description. For example Aria's survey packages allow the response to questions to be specified along with the page. The response element is used by the Question component and does not itself create a component

The addElement method used for such implementations. The method is only called if no factory chooses to construct a component for the element. The essential difference from the previous method is that the addElement does not add a component to the page.

Lines 83-87

This method is used by the component factory and must be provided by all factories. The package name is that of the components rather than the factory itself.

Using the New Components

  1. <Question name="Q1" x="0" y="100" w="640" h="60" id="1001" content="Is Aria a great Java application framework?" cue="cue.gif">
  2. <Response content="Yes" id="1"/>
  3. <Response content="No" id="2"/>
  4. <Response content="No comment" id="2"/>
  5. </Question>

Sometimes it is useful to register components without first having to create a component factory. Components can be registered by adding entries to an XML file. The RegisteredComponentFactory can be used to load components specified in this way.

A sample component

To demonstrate how easy it is to include a new component in Aria we have included the source for a test component in the Aria Editor source distribution at test.Formaria.components.TestShape . The component doesn't do very much except paint a few trivial shapes. Here's the source code:

TestShape a sample component

  1. package test.Formaria.components;
  2.  
  3. import java.awt.Dimension;
  4. import java.awt.Graphics;
  5. import javax.swing.JComponent;
  6.  
  7. import org.formaria.aria.AttributedComponent;
  8.  
  9. /**
  10.   * Draws a simple shape
  11.   * <p>Copyright: Copyright Formaria Ltd. (c) 2001-2005</p>
  12.   * <p>License: see license.txt</p>
  13.   * <p>$Revision: 1.1 $</p>
  14.   */
  15. public class TestShape extends JComponent implements XAttributedComponent
  16. {
  17. protected int shape = 0;
  18.  
  19. public static final int RECTANGLE = 0;
  20. public static final int ORTHO_LINE = 1;
  21. public static final int EXTRABOLD_HORIZONTAL = 2;
  22. public static final int BOLD_HORIZONTAL = 3;
  23. public static final int NORMAL_HORIZONTAL = 4;
  24. public static final int THIN_HORIZONTAL = 5;
  25. public static final int EXTRABOLD_VERTICAL = 6;
  26. public static final int BOLD_VERTICAL = 7;
  27. public static final int NORMAL_VERTICAL = 8;
  28. public static final int THIN_VERTICAL = 9;
  29. public static final int RIGHT_TOP_LINE = 10;
  30. public static final int LEFT_TOP_LINE = 11;
  31. public static final int ELLIPSE = 12;
  32. public static final int SOLID_ELLIPSE = 13;
  33. public static final int SOLID_DIAMOND = 14;
  34. public static final int DIAMOND = 15;
  35.  
  36. /**
  37.   * Constructor for a new Shape
  38.   */
  39. public TestShape()
  40. {
  41. }
  42.  
  43. /**
  44.   * Fills the shape with the background color
  45.   * @param g the graphics context
  46.   */
  47. public void paintComponent( Graphics g )
  48. {
  49. super.paintComponent( g );
  50.  
  51. Dimension d = getSize();
  52. g.setColor( getForeground());
  53.  
  54. switch( shape ) {
  55. case RECTANGLE:
  56. g.fillRect( 0, 0, d.width, d.height );
  57. break;
  58. case ORTHO_LINE:
  59. g.fillRect( 0, 0, getSize().width, getSize().height );
  60. break;
  61.  
  62. case EXTRABOLD_HORIZONTAL:
  63. g.drawLine( 0, 0 +3, d.width, 3 );
  64. case BOLD_HORIZONTAL:
  65. g.drawLine( 0, 0 +2, d.width, 2 );
  66. case NORMAL_HORIZONTAL:
  67. g.drawLine( 0, 0 +1, d.width, 1 );
  68. case THIN_HORIZONTAL:
  69. g.drawLine( 0, 0, d.width, 0 );
  70. break;
  71.  
  72. case EXTRABOLD_VERTICAL:
  73. g.drawLine( 3, 0, 3, d.height );
  74. case BOLD_VERTICAL:
  75. g.drawLine( 2, 0, 2, d.height );
  76. case NORMAL_VERTICAL:
  77. g.drawLine( 1, 0, 1, d.height );
  78. case THIN_VERTICAL:
  79. g.drawLine( 0, 0, 0, d.height );
  80. break;
  81.  
  82. case EXTRABOLD_VERTICAL:
  83. g.drawLine( 3, 0, 3, d.height );
  84. case BOLD_VERTICAL:
  85. g.drawLine( 2, 0, 2, d.height );
  86. case NORMAL_VERTICAL:
  87. g.drawLine( 1, 0, 1, d.height );
  88. case THIN_VERTICAL:
  89. g.drawLine( 0, 0, 0, d.height );
  90. break;
  91.  
  92. case RIGHT_TOP_LINE:
  93. g.drawLine( d.width, 0, 0, d.height );
  94. break;
  95. case LEFT_TOP_LINE:
  96. g.drawLine( 0, 0, d.width, d.height );
  97. break;
  98.  
  99. case ELLIPSE:
  100. g.drawOval( 0, 0, d.width, d.height );
  101. break;
  102. case SOLID_ELLIPSE:
  103. g.fillOval( 0, 0, d.width, d.height );
  104. break;
  105.  
  106. case SOLID_DIAMOND:
  107. case DIAMOND:
  108. {
  109. int[] xpts = new int[ 4 ];
  110. int[] ypts = new int[ 4 ];
  111. xpts[ 0 ] = 0 + d.width / 2;
  112. xpts[ 1 ] = 0 + d.width;
  113. xpts[ 2 ] = 0 + d.width / 2;
  114. xpts[ 3 ] = 0;
  115. ypts[ 0 ] = 0;
  116. ypts[ 1 ] = 0 + d.height / 2;
  117. ypts[ 2 ] = 0 + d.height;
  118. ypts[ 3 ] = 0 + d.height / 2;
  119. if ( shape == SOLID_DIAMOND )
  120. g.fillPolygon( xpts, ypts, 4 );
  121. else
  122. g.drawPolygon( xpts, ypts, 4 );
  123. }
  124. break;
  125.  
  126. }
  127. }
  128.  
  129. /**
  130.   * Get shape identifier. The ID is an enumerated constant
  131.   * @param shapeId the new shape ID.
  132.   */
  133. public void setShape( int shapeId )
  134. {
  135. shape = shapeId;
  136. repaint();
  137. }
  138.  
  139. /**
  140.   * Get shape identifier.
  141.   * @return the enumerated constant for this shape
  142.   */
  143. public int getShape()
  144. {
  145. return shape;
  146. }
  147. /**
  148.   * Set one or more attributes of the component. Currently this handles the
  149.   * attributes:
  150.   * <OL>
  151.   * <LI>shape, value=1 to 15</LI>
  152.   * </OL>
  153.   * @param attribName the attribute name
  154.   * @param attribValue the attribute value
  155.   */
  156. public void setAttribute( String attribName, String attribValue )
  157. {
  158. if ( attribName.compareTo( "shape" ) == 0 )
  159. shape = new Integer( attribValue ).intValue();
  160.  
  161. repaint( 100 );
  162. }
  163. }

There isn't an awful lot to say about this component as it does so little and the details of the painting are unimportant. What is important though is the interface it offers for programming within Aria.

The component paints a shape which is dictated by the shape property. This property has a getter and a setter method ( getShape and setShape ).

The component also implements the XAttributedComponent method to assist the setting up of the component via XML. The setAttribute method required by this interface sets the value of an attribute, in this case the ` shape ' attribute.

If we then build this component into a Jar file for redistribution it can be used within Aria. Using the component registry editor we can add the file and select the Shape property for inclusion. This process generates the following XML:

The XML registration for the test component.

  1. <Components>
  2. <Jars>
  3. <Jar path="C:\CVS\AriaEditor\dist\TestComponents.jar" project="MyProject"/>
  4. </Jars>
  5. <Component name="TestShape" class="test.Formaria.components.TestShape" icon="" ui="Swing">
  6. <Property name="Shape" mode="Normal" type="both">
  7. <Param type="int"/>
  8. </Property>
  9. </Component>
  10. </Components>

Special components

Sometimes the component factories may not be enough to allow you handle the needs of an individual component. However, as a Java framework you can employ the normal programming techniques to implement your functionality. This can take you from one line code fragments for setting an esoteric component attribute to complete sets of custom classes. You can freely mix Aria code, Aria XML and normal Java and to illustrate this ability to extend the framework lets look at how to add a web browser component.

Hooking up a web browser

Using the Java.net JDIC project (http://jdic.dev.java.net) we can hook up an instance of the desktop's default web browser. In this example the browser will be embedded in a popup window, an instance of the Dialog class and therefore we need to subclass Dialog :

Embed a web browser.

  1. package org.formaria.mypackage;
  2.  
  3. import java.net.URL;
  4.  
  5. import java.awt.BorderLayout;
  6. import java.awt.Panel;
  7.  
  8. import org.jdesktop.jdic.browser.WebBrowser;
  9. import org.formaria.swing.Dialog;
  10. import org.formaria.aria.data.BaseModel;
  11. import org.formaria.aria.data.Model;
  12. import javax.swing.SwingUtilities;
  13. import org.jdesktop.jdic.browser.WebBrowserListener;
  14. import org.jdesktop.jdic.browser.WebBrowserEvent;
  15. import java.net.URLConnection;
  16.  
  17. /**
  18.   * Show more information about a component
  19.   * <p>Copyright Formaria Ltd. (c) 2003-2004 </p>
  20.   * <p>License: see license.txt </p>
  21.   * $Revision: 1.3 $ not attributable
  22.   */
  23. public class BrowserDialog extends Dialog implements WebBrowserListener
  24. {
  25. DataModel selectedNode;
  26. int nodeNumber;
  27. Dialog webDialog;
  28. WebBrowser webBrowser;
  29.  
  30. public BrowserDialog()
  31. {
  32. }
  33.  
  34. public void pageActivated()
  35. {
  36. SwingUtilities.invokeLater( new Runnable()
  37. {
  38. public void run()
  39. {
  40. website();
  41. }
  42. } );
  43. }
  44.  
  45. public void done()
  46. {
  47. if ( wasMouseClicked() ) {
  48. stop();
  49. closeDlg();
  50. }
  51. }
  52.  
  53.  
  54. /**
  55.   * Show the website
  56.   */
  57. public void website()
  58. {
  59. try {
  60. // Check if the net is accessible
  61. URL url = new URL( "http://www.Formaria.com" );
  62. URLConnection conn = url.openConnection();
  63. if ( conn != null ) {
  64. conn.connect();
  65.  
  66. String websiteUrl = ( String ) getAttribute( "url", "websiteBtn" );
  67. webDialog = new Dialog( false, 0 );
  68. webDialog.setCaption( "Internet content" );
  69. webDialog.setModal( true );
  70.  
  71. //Use below code to check the status of the navigation process,
  72. //or register a listener for the notification events.
  73. webBrowser = new WebBrowser();
  74. webBrowser.setDebug( true );
  75. webBrowser.addWebBrowserListener( this );
  76. WebBrowser.Status myStatus = webBrowser.getStatus();
  77.  
  78. try {
  79. webBrowser.setURL( new URL( websiteUrl ) );
  80. }
  81. catch ( Exception e ) {
  82. System.out.println( e.getMessage() );
  83. return;
  84. }
  85.  
  86. Panel panel = new Panel();
  87. panel.setLayout( new BorderLayout() );
  88. panel.setLocation( 0, 0 );
  89. panel.setSize( 700, 500 );
  90. panel.add( webBrowser, BorderLayout.CENTER );
  91.  
  92. webDialog.setLayout( new BorderLayout() );
  93. webDialog.getContentPane().add( panel, BorderLayout.CENTER );
  94. webDialog.setSize( 700, 500 );
  95. webDialog.doLayout();
  96. webDialog.showDialog( parent );
  97. closeDlg();
  98. }
  99. }
  100. catch ( Exception ex ) {
  101. }
  102. }
  103.  
  104. private void stop()
  105. {
  106. if ( webBrowser != null ) {
  107. webBrowser.stop();
  108. webBrowser.setVisible( false );
  109. webBrowser = null;
  110. }
  111. }
  112.  
  113. // Methods from WebBrowserListener
  114. public void downloadStarted(WebBrowserEvent webBrowserEvent)
  115. {}
  116.  
  117. public void downloadCompleted(WebBrowserEvent webBrowserEvent)
  118. {
  119. // webDialog.hide();
  120. }
  121.  
  122. public void downloadProgress(WebBrowserEvent webBrowserEvent)
  123. {}
  124.  
  125. public void downloadError(WebBrowserEvent webBrowserEvent)
  126. {}
  127.  
  128. public void titleChange(WebBrowserEvent webBrowserEvent)
  129. {}
  130.  
  131. public void statusTextChange(WebBrowserEvent webBrowserEvent)
  132. {}
  133. }

Much of the above code is dictated by the WebBrowserListener interface but it is worth noting a couple of points in relation to the code. First the pageActivated method use the SwingUtilities invokeLater method to delay display of the web page as the download of the web content may take considerably longer than displaying the dialog. Secondly the code calls the web browser's setDebug( true ) method to turn on extra diagnostics. The method causes additional information to be output to the console and this can be quite useful given the number of components involved in the process of displaying the web page and the fact that some of these components are native or operating system components and fall outside of the normal realm of debugging for Java programs.

Using third party components in this way also introduces issues with regard to distribution. While the development environment may allow you to include all the various sub components needed design and build your application yo may need to take some additional steps to ensure that the component can be properly used on end user systems. This additional requirement may simply be to include some additional Jar files or it may be more complex. Unfortunately Aria can do little of this work for you but once you have figured out the requirements it should be possible to include the additional step(s), including the steps necessary to build the distribution files within your project's ANT build file.

Adding a splash screen

The customization of a component is not restricted to creation time and you can continue to interact and customize a component as you would in a typical Java application. To demonstrate this capability we will create an application splash screen that automatically dismisses itself after five seconds. (Please note that as of Version 3.0 with the Swing widget set there is an easier way to create a simple splash screen and this is demonstrated at the end of this chapter).

To display a splash screen for an application the simplest method is to invoke the display of a popup dialog in your application's first page. The process requires a small amount of Java coding. The display code can be embedded in the first page's pageActivated method as below:

Launch a splash screen

  1. private static boolean splashActivated = false;
  2.  
  3. public void pageActivated()
  4. {
  5.  
  6. // This flag prevents the splash screen being redisplayed if the first page is reactivated
  7. // or shown again.
  8. if ( !splashActivated ) {
  9. showVersionInfoDialog( true );
  10. splashActivated = true;
  11. }
  12. }
  13.  
  14. private Dialog showVersionInfoDialog( boolean modal )
  15. {
  16. final Dialog popupDialog = (Dialog)pageMgr.loadPage( "SplashScreen" );
  17. final Page thisPage = this;
  18. final boolean isModal = modal;
  19.  
  20. SwingUtilities.invokeLater( new Runnable()
  21. {
  22. public void run()
  23. {
  24. // Only needed if the content contains bound controls.
  25. popupDialog.updateBindings();
  26.  
  27. popupDialog.setModal( isModal );
  28.  
  29. // Force the dialog to calculate its size
  30. popupDialog.pack();
  31. popupDialog.setSaveOnClose( false );
  32.  
  33. // Sleep for 5 seconds and then close the dialog - only for the `modeless' version.
  34. // Change or remove the next if statement if you want a model splash screen.
  35. // to be dismissed on the timer
  36. if ( !isModal ) {
  37. new Thread()
  38. {
  39. public void run()
  40. {
  41. long startTime = new Date().getTime();
  42. try {
  43. // Loop so that the dialog can repaint itself once any images have loaded
  44. while (( new Date().getTime() - startTime ) < 5000L ) {
  45. popupDialog.repaint();
  46. Thread.currentThread().sleep( 100 );
  47. }
  48. }
  49. catch ( Exception ex ) {}
  50. popupDialog.closeDlg();
  51. }
  52. }.start();
  53. }
  54. popupDialog.showDialog( thisPage, "Welcome to my application", null );
  55.  
  56. }
  57. } );
  58. return popupDialog;
  59. }

The showVersionInfoDialog method loads the SplashScreen page (it can be setup like any other dialog/page) and then starts a background thread that will dismiss the dialog after a sleep period of 5000 milliseconds.

Finally in implementing the actual splash screen it is important to note that the page being displayed is assumed to be an instance of Dialog and as such it needs to derived from the Dialog class instead of Page . If you are using XML don't forget to specify the class in the page declaration as follows:

Specify the Dialog class for your splash screen in XML

  1. <Page class="org.formaria.swing.Dialog">

Like in the case of the web browser the interesting code embedded in a call to invokeLater .

Dropping to Java can help work around many limitations and solve many more problems than the above examples. In fact you should be able to employ just about any type of component or component feature within your Aria application. And remember, where XML is insufficient you can supplement it with Java.

Swing splash screen

In the Swing version of the Aria libraries there is a class XSplashWindow that can be used to display a simple splash screen. The splash window is loaded prior to the rest of the Aria framework being loaded and therefore not all the resource handling of the framework is available to this class. In fact the class loads and then invoked the main method in the framework proper as we will see below. The sequence of execution is thus, that firstly the SplashWindow class is invoked and it in turn starts the class passed as an argument (or Applet if none is specified), which should be the entry point to the Aria framework (which, of course depending on the application style being used).

The the application is invoked as follows:

Starting with a splash screen

  1. java -cp ./lib/AriaRuntimeCommon.jar org.formaria.swing.splash.SplashWindow <arglist>

where is the argument list that would otherwise be passed to the Applet , or whatever was the normal startup class for your application. The application also needs to find the file splash.gif , which should be located on the classpath. The splash screen functionality can be found in a number of the examples that ship with Aria. The splash screen functionality is also setup by the project wizard, by choosing the

The results really depend on what you add as the splash image. The splash screen can also be customized by subclassing the SplashWindow class, and in the image below this has been done to add transparency support

Component customization

Aria 3.0 adds a new facility for customizing components such that a collection of method calls can be made to customize a component and set properties on that component. The facility is itself customizable, supporting adapters to control how the properties are applied to the component. The customizations can be applied post creation of the component or just before activation of the page, when the component has been populated.

For example in the case of a table the property may apply to the table columns rather than the table itself. In the example below a table is customized in this way:

Setting atable customizer

  1. <ScrollPane>
  2. <Table2 name="currentAED" .... customizer="tableCustomizer"/>
  3. </ScrollPane>

the customizer attribute refers to a customization specified in the customizations.xml file. The customization file is as follows:

A sample table customization

  1. <customizer>
  2. <Adaptors>
  3. <Adaptor name="ColumnAdaptor" class="org.formaria.registry.TableColumnCustomizer"/>
  4. </Adaptors>
  5. <PropertySets>
  6. <PropertySet name="tableCustomizer" when="1">
  7. <AutoCreateColumnsFromModel args="false"/>
  8. <GridColor args="white"/>
  9. <ViewportBackground args="white"/>
  10. <Background target="parent"
  11. value="white"/>
  12. <HeaderRenderer adaptor="ColumnAdaptor"
  13. scope="*"
  14. class="org.formaria.swing.table.TableHeaderRenderer"
  15. args="getTableHeader()"/>
  16. <CellRenderer adaptor="ColumnAdaptor"
  17. scope="0-6"
  18. class="org.formaria.swing.table.AltRowTableCellRenderer">
  19. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  20. <AltUnselectedColors args="0,0,102;252,252,255"/>
  21. </CellRenderer>
  22.  
  23. <CellRenderer adaptor="ColumnAdaptor" scope="N"
  24. class="org.formaria.swing.table.AltRowTableCellRenderer">
  25. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  26. <AltUnselectedColors args="0,0,102;252,252,255"/>
  27. <WrapsText args="true"/>
  28. </CellRenderer>
  29. <AutoResizeMode args="1"/><!--JTable.AUTO_RESIZE_NEXT_COLUMN -->
  30. </PropertySet>
  31. </PropertySets>
  32. </customizer>

The result of the above customization is a table like the following with row striping and customized headers:

Using Drag and Drop

Drag and drop supported has been added for selected Swing components. Support is enabled by adding dragEnabled="true" as an attribute. Additional support can be registered by adding a transferhandlers.xml file to the project. The setup is similar to that of the setup of data bindings.

Drag and drop configuration

  1. <Bindings>
  2. <InspectorTransferHandlers>
  3. </InspectorTransferHandlers>
  4.  
  5. <ClassTransferHandlers>
  6. <TransferHandler target="org.formaria.swing.List"
  7. class="org.formaria.swing.dnd.ModelTransferHandler"
  8. type=""/>
  9. </ClassTransferHandlers>
  10.  
  11. <InterfaceTransferHandlers>
  12. <!-- This does not actually use a binding -->
  13. <!--TransferHandler target="org.formaria.aria.ModelHolder">
  14. <onError fixError="DataSourceClassMissing"/>
  15. </Binding-->
  16. </InterfaceTransferHandlers>
  17.  
  18. <InstanceTransferHandlers>
  19.  
  20. <TransferHandler target="org.formaria.swing.Image"
  21. class="org.formaria.swing.dnd.ModelTransferHandler"
  22. type=""/>
  23. </InstanceTransferHandlers>
  24. </Bindings>

Using SVG graphics

Scaleable Vector Graphics ( SVG) is a vector file format from the W3C. SVG is a widely supported, resolution independant graphics file format that can be used with Aria and Aria.

SVG images can be created by experienced graphic designers and provide a relatively compact format. Sympathic use of SVG graphics can greatly enhance the overall visual impact of your application. Aria provides several painter classes, components and evan a Swing look and feel that support SVG. However, SVG is not just for eye candy, and rich, iteractive components can be built into your application to provide advanced, eye catching features.

Installing the components

Aria's SVG Components are designed for use in a wide range or Rich Internet Applications for purposes such as mapping, facilities management, interactive drawings and illustration. A rich developer API is provided that integrates with other Aria components and services. The API is designed to allow easy access to backend services so that the SVG components may be used for easy visualization of complex spatial data or just to add some extra interaction to your application.

Component details

DisplaySvg

A JComponent class that can display SVG images.

GrabMap

The GrabMap class provide a means of dragging an SVG image in conjunction with the SvgImageMap class using the mouse.

The component can be added to an application that uses the SvgImageMap quite easily, as shown below.

Setting up a grab map

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4.  
  5. public void pageCreated()
  6. {
  7. svgImage = new SvgImageMap();
  8. svgImage.setURL( ClassLoader.getSystemResource( "images/image1.svg" ));
  9. GrabMap grabMap = new GrabMap( svgImage );
  10. }
  11. }
GroupHierarchy

The GroupHierarchy class extracts the group hierarchy from and SVG image. The group hierarchy is then stored in a DefaultMutableTreeNode instance so that the group hierarchy can be displayed in a tree component.

The code example below shows how the how the component can be used in a program by passing it an instance of the SvgImageMap and then diplaying the results in a JTree within a JFrame .

Setting up a group hierarchy

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4. public void pageCreated()
  5. {
  6. svgImage = new SvgImageMap();
  7. svgImage.setURL( ClassLoader.getSystemResource( "images/image1.svg" ));
  8.  
  9. GroupHierarchy gh = new GroupHierarchy( svgImage );
  10. DefaultMutableTreeNode root = gh.getGroupHierarchy();
  11. JTree tree = new JTree( root );
  12.  
  13. JFrame frame = new JFrame();
  14. frame.setSize( 375, 400 );
  15. frame.setLocation( 100, 100 );
  16. frame.getContentPane().add( new JScrollPane( tree ),
  17. BorderLayout.CENTER );
  18. frame.setVisible( true );
  19. }
  20. }
HotSpotInfo

An interface that can be implemented by any class that needs to have data passed to it from InfoFileParser class.

InfoFileParser

Used to parse an xml file and pass the information to a class implmenting the StatusInfo interface.

MapArea

Defines an area's bounds based on a set of points.

OverlayComponent

A non-opaque component, in which other components can be added, that can be used as an overlay.

PointSystem

The PointSystem class allows conversion of a coordinate from the svg coordinate system to the application coordinate system and vice versa. It can be used in an application as follows.

Use a point system

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4.  
  5. public void pageCreated()
  6. {
  7. svgImage = new SvgImageMap();
  8. svgImage.setURL( ClassLoader.getSystemResource( "images/image1.svg" ));
  9. PointSystem pointSystem = new PointSystem( svgImage );
  10. Point2D p1 = pointSystem.glassPaneToSvg( 100.0, 200.0 );
  11. }
  12. }
PopUpWindow

A pop-up window that can be used to display textual information.

RenderingSemephore

Used to insure that seperate calls to render the SVG image occur independently.

RolloverComponent

The RolloverComponent class is used to display a Swing component on the glass pane when a mouse rollover event occurs.

It can be added to an application as shown below. Setting up a rollover

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4. protected JComponent glassPane;
  5. protected RolloverComponent rc;
  6.  
  7. public void pageCreated()
  8. {
  9. glassPane = (JComponent)( (Applet)project.getStartupObject()
  10. ).getGlassPane();
  11. glassPane.setOpaque( false );
  12. glassPane.setLayout( new BorderLayout() );
  13.  
  14. svgImage = new SvgImageMap();
  15. svgImage.setURL( ClassLoader.getSystemResource(
  16. "images/image1.svg"));
  17.  
  18. Graph graph = new Graph();
  19.  
  20. rc = new RolloverComponent( new JComponent[]{ graph } );
  21. rc.setBounds( 349, 233, 11, 11 );
  22. glassPane.add( rc );
  23. }
  24. }

In the above example, when a mouse rollover event occurs on the RolloverComponent , the Graph component will be displayed on the glass pane.

RoutePlotter

Used to plot a route within an SVG image using a series of waypoints.

SelectionZoom

The SelectionZoom class allows zooming of the Svg image displayed in the SvgImageMap class by selecting a region using the CTRL key and the mouse.

It can be added to an application as shown below.

Adding a selection area

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4.  
  5. public void pageCreated()
  6. {
  7. svgImage = new SvgImageMap();
  8. svgImage.setURL( ClassLoader.getSystemResource( "images/image1.svg" ));
  9. SelectionZoom zoomSelection = new SelectionZoom( svgImage );
  10. }
  11. }
SelectSvgArea

Used to select a region within an svg image an return all elements within that region.

SvgImageMap

The SvgImageMap class manages the display of an SVG image. The class extends the Aria Panel class to provide a panel within which the SVG image can be displayed.

The SvgImageMap can be added to a page as follows:

Adding an SvgImageMap component

  1. <SvgImageMap name="imageMap1" x="50" y="50" w="200" h="150"
  2. content="images/image1.svg">

Or via Java code as follows: Adding an SvgImageMap via Java

  1. public class Images extends Page
  2. {
  3. protected XPanel panel;
  4. protected SvgImageMap svgImage;
  5.  
  6. public void pageCreated()
  7. {
  8. Panel panel = (Panel)findComponent( "panel1" );
  9. svgImage = new SvgImageMap();
  10. svgImage.setURL( ClassLoader.getSystemResource( "images/image1.svg" ));
  11. panel.add( svgImage );
  12. }
  13. }
SvgLayer

A layer, which elements can be added to, that can be added to an svg image.

SvgMagnifyingWindow

The SvgMagnifyingWindow class provides a means of zooming an SVG image in conjunction with the SvgImageMap class. The class provides a component for displaying the zoomed SVG image.

Below is an example of setting up the magnifying window in a page. First the window is added through XML:

Adding an SvgMagnifyingWindow via XML

  1. <SvgMagnifyingWindow name="magWindow" x="0" y="0" h="180" w="200" background="white" >

Then the remaining set-up work is done in the pageCreated method and using the mouseMoved method of the MouseMotionListener interface: Setting up the magnifier

  1. public class Magnification extends Page implements MouseMotionListener
  2. {
  3. protected SvgImageMap imageMap;
  4. protected SvgMagnifyingWindow magWindow;
  5.  
  6. public void pageCreated()
  7. {
  8. imageMap = (SvgImageMap)findComponent("imageMap");
  9. magWindow = (SvgMagnifyingWindow)findComponent("magWindow");
  10.  
  11. imageMap.addMouseMotionListener( this );
  12.  
  13. magWindow.setBorder( BorderFactory.createLineBorder(
  14. Color.GRAY, 2 ));
  15. magWindow.setDiagram( imageMap.getSvgDiagram() );
  16. magWindow.setImageMap( imageMap );
  17. magWindow.setScaleFactor( 5.0F );
  18. }
  19.  
  20. public void mouseMoved( MouseEvent e )
  21. {
  22. magWindow.setZoomPoint( e.getPoint() );
  23. }
  24. }
SvgMagnifyingGlass

The SvgMagnifyingGlass class provides a means of zooming an SVG image in conjunction with the SvgImageMap class. It differs from the SvgMagnifyingWindow in that it provides a magnifying glass which can be scrolled over the image using the mouse. The component is displayed on the glass pane and the zoomed area of the SVG image is displayed in the magnifying glass. The class provides a component for displaying the zoomed SVG image.

Then set-up work is done in the pageCreated method, the mouseMoved method of the MouseMotionListener interface and the mouseEntered and mouseExited methods of the MouseListener interface:

Setup the magnifying glass

  1. public class Magnification extends Page implements MouseMotionListener,
  2. MouseListener
  3. {
  4. protected SvgImageMap imageMap;
  5. protected SvgMagnifyingGlass magGlass;
  6. protected JComponent glassPane;
  7.  
  8. public void pageCreated()
  9. {
  10. imageMap = (SvgImageMap)findComponent( "imageMap" );
  11. magGlass = new SvgMagnifyingGlass();
  12. glassPane = imageMap.getGlassPane();
  13. glassPane.setLayout( new BorderLayout() );
  14. imageMap.addMouseMotionListener( this );
  15. imageMap.addMouseListener( this );
  16.  
  17. magGlass.setDiagram( imageMap.getSvgDiagram() );
  18. magGlass.setImageMap( imageMap );
  19. magGlass.setScaleFactor( 5.0F );
  20. glassPane.add( magGlass );
  21. new Thread( magGlass ).start ();
  22. glassPane.setVisible( true );
  23. }
  24.  
  25. public void mouseMoved( MouseEvent e )
  26. {
  27. magGlass.setZoomPoint( e.getPoint(), e.getPoint() );
  28. }
  29.  
  30. public void mouseEntered( MouseEvent e ){
  31. magGlass.setAnimationMode ( SvgMagnifyingGlass.FADE_IN, 20F );
  32. }
  33.  
  34. public void mouseExited( MouseEvent e )
  35. {
  36. magGlass.setAnimationMode ( SvgMagnifyingGlass.FADE_OUT, 10F );
  37. }
  38. }

In the above code example the mouseEntered and mouseExited methods trigger the fade-in and fade-out animations for the magnifying glass. The float value passed to the setAnimationMode method sepecifies the duration if the animation.

SvgMagnifier

The SvgMagnifier class uses the SvgMagnifyingGlass and SvgMagnifyingWindow classes to magnify an SVG image.

A window is provided for displaying the magnification using SvgMagnifyingWindow . Radio buttons are provided for switching between magnification modes i.e. Magnifying window and magnifying glass. Check boxes are provided for switching on the rotation and high quality rendering modes of the magnifying glass during run time.

Setting up the Magnifier

  1. public class Magnification extends Page implements Mouselistener,
  2. MouseMotionListener
  3. {
  4. protected SvgMagnifier magnifier;
  5. protected JComponent glassPane;
  6.  
  7. public void pageCreated()
  8. {
  9. glassPane =
  10. (JComponent)( (Applet)project.getStartupObject()
  11. ).getGlassPane();
  12.  
  13. glassPane.setOpaque( false );
  14. glassPane.setLayout( new BorderLayout() );
  15. SvgImageMap svgImage = (SvgImageMap)findComponent("svgImage");
  16. svgImage.getSvgPanel ().addMouseMotionListener (this);
  17. magnifier = new SvgMagnifier( glassPane, svgImage );
  18.  
  19. svgImage.addMouseMotionListener( this );
  20. svgImage.addMouseListener( this );
  21. }
  22.  
  23. public void mouseMoved( MouseEvent e )
  24. {
  25. magnifier.setZoomPoint( e.getPoint());
  26. }
  27.  
  28. public void mouseDragged( MouseEvent e )
  29. {
  30. magnifier.setZoomPoint( e.getPoint());
  31. }
  32.  
  33. public void mouseEntered (MouseEvent e)
  34. {
  35. magnifier.setMagGlassAnimation( SvgMagnifyingGlass.FADE_IN,
  36. 20F );
  37. }
  38.  
  39. public void mouseExited (MouseEvent e)
  40. {
  41. magnifier.setMagGlassAnimation( SvgMagnifyingGlass.FADE_OUT,
  42. 10F );
  43.  
  44.  
  45. }
  46. }
SvgRolloverFinder

An interface containing methods that can be implemented to aid the setup of rollover blocks.

ViewBoxListener

The ViewBoxListener class works in conjunction with the SvgImageMap to ensure components displayed on the glass pane above a certain point of image map remain at that location if the image map's view box is modified. This is achieved by using the PointSystem class to convert from the SVG co-ordinate system to the glass pane co-ordinate system. An example of its use in an application is shown below.

Setting up a ViewBoxListener

  1. public class Welcome extends Page
  2. {
  3. protected SvgImageMap svgImage;
  4. protected JComponent glassPane;
  5.  
  6. public void pageCreated()
  7. {
  8. glassPane = (JComponent)( (Applet)project.getStartupObject()
  9. ).getGlassPane();
  10. glassPane.setOpaque( false );
  11. glassPane.setLayout( new BorderLayout() );
  12.  
  13. svgImage = new SvgImageMap();
  14. svgImage.setURL( ClassLoader.getSystemResource(
  15. "images/image1.svg"));
  16.  
  17. JLabel label1 = new JLabel( "Label 1" );
  18. label1.setBounds( 200, 200, 50, 20 );
  19. glassPane.add( label1 );
  20.  
  21. ViewBoxListener listener = new ViewBoxListener( imageMap );
  22. listener.registerComponent( label1 );
  23. }
  24. }
ViewBoxModifier

Used to modify the viewbox size and position.

Waypoint

A waypoint representing a point within an SVG image.

WaypointFlag

A flag that can be added to an SVG image to mark a waypoint.

Drag and Drop

Drag and drop is a powerful and intuitive feature for some applications, allowing the user to visually move components and data about within the applications. For applications such as shoping cart and retail applications drag and drop can add that extra polish. Enabling drag and drop

For the most part all you need do to drag and drop enable a component is to set the dragEnabled attribute of the component.

Drag and drop setup

  1. <Panel layout="border">
  2. <Image name="dragSource" dragEnabled="true" constraint="west"/>
  3. <Image name="dropTarget" dragEnabled="true" constraint="east"/>
  4. </Panel>

The application itself needs to be drag enabled also, and this is just a matter of registering the require drag and drop factory to the application, just instantiate and instance of TransferHandlerFactory in somewhere in your application (typically the constructor of the first page):

Make the application drag and drop enabled

  1. public Welcome()
  2. {
  3. new TransferHandlerFactory( project );
  4. }

Drag and drop functionality can be implemented within a page by having that page implement the DragManager interface. The interface provides a getDragInfo method for supplying info to the drop target from the dragged component.

To illustrate a more advanced use of drag and drop we will take a look at the shopping cart from the SVG Components demo shown below:

A code example of the implementation of the interface and the getDragInfo method is shown below.

Setting up drag and drop data

  1. public class Catalog extends Page implements DragManager
  2. {
  3. protected HashMap infoMap;
  4.  
  5. public Catalog()
  6. {
  7. infoMap = new HashMap();
  8. String [] keys = { DragInfo.TITLE, DragInfo.KEY, "price", "image",
  9. DragInfo.DESCRIPTION };
  10.  
  11. Object [][] v = {
  12. {"Canon Digital Camera", "camera", "$249.99", "Camera.jpg",
  13. "The Canon PowerShot SD550 is a sleek 7 megapixel ultracompact." },
  14. { "Netgear Network Storage Device", "netstore", "$99.99", "networkstorage.jpg",
  15. "NETGEAR's Storage Central is an innovation for storing and protecting files." },
  16. { "Netgear Wireless Card", "wireless", "$39.99", "wirelesscard.jpg",
  17. "With the RangeMax NEXT Notebook adapter inserted, your PC will maintain a steady, constant network connection." }};
  18.  
  19. for (int i = 0; i < v.length; i++) {
  20. DragInfo dragInfo = new DragInfo();
  21. dragInfo.addValues( keys, v[ i ] );
  22. infoMap.put( (String)v[i][1], dragInfo );
  23. }
  24. }
  25.  
  26. public DragInfo getDragInfo( String key )
  27. {
  28. return (DragInfo)infoMap.get( key );
  29. }
  30. }

Each instance of the DragInfo class stores information for a draggable component. When the component is dropped on the target, the ModelTransferHandler class uses the components dragInfo property to query the hash map and extract the DragInfo object assigned to the component. An example of setting the dragInfo property on a component is shown below.

Enabling the components

  1. <CaptionedImage dragInfo="camera" imagename="Camera.jpg"
  2. caption="Digital Camera" x="15" y="50" w="120" h="120"
  3. dragEnabled="true" />

The DataModelTransferHandler 's createTransferable method is responsible for extracting the relevant DragInfo instance from the class implementing the DragManager interface and then creating a new transferable. The transferable is then passed to the drop target using the importData method of the DataModelTransferHandler class. The drag info is then extracted from the transferable and passed to the drop component.

Drag and drop functionality allows you to setup components bindings when builing pages in the Editor. In order to do that you need to select a model node from the data model hierarchy dispplayed in the data visualizer and simply drag and drop it to a component on a page. For more information on drag and drop data bindings please see Binding to database tables..

The following components are drag and drop supported under Aria: List, Image and Tree

More information on drag and drop components can be found on the Java Swing website under "Introduction to Drag and Drop and Data Transfer". Information can also be found on the Aria wiki.

Drag and drop functionality can be extended to include custom components.

Registering new flavours for drag and drop is handled registering a suitable transfer handler in the transferhandlers.xml file. It is a similar process to setting up a new data binding type. For more info see Adding a new binding type.

To drag enable a new component the 'dragEnabled' tag must be set on the component as shown in the 'Customizing drag and drop section above'.Then a suitable TransferHandler needs to be set up for the component.

Printing and Export

Printouts can be produced in a number of different ways, directly and indirectly through applications like Excel and OpenOffice. Aria provides some easy to use classes that can be used to print pages or framesets from an application. Custom printed pages can also be to create a sort of report generation.

Direct printing

Aria's pages can be printed directly using the built-in infrastructure. However it should be noted that this may not be appropriate in all situations. A page created for on-screen, landscape rendering may not be best suited to the high resolution, portrait format of most printers. Aria applications can also include special printout pages where the layout and aspect can be given a more suitable configuration. Using the data binding schemes promoted by Aria this should involve little additional work while at the same time adding versatility to the printout infrastructure.

The printing infrastructure

Printing in Aria uses the normal Java printing API and mechanisms, but in order to hide some of the low level details Aria provides some additional infrastructure. The printing infrastructure revolves around three classes in the org.formaria.print package. These classes are:

  1. The Printing Classes

Printout

This class encapsulates the print job. Control of headers and footers is also provided.

PrintablePage

A decoration of the Page class to support printing. Scales the page as needed.

PrintableFrameSet

A decoration of a frameset to allow printing of the frameset's pages. Again scaling is performed as needed.

These classes act as decorations of the Page class so that the pages can be configured for on-screen use or for printing or they can be used for both purposes. This decoration also means that the pages can be designed as any other page with little need for special considerations.

The use of these classes are as follows:

Printing a Frameset

  1. /**
  2.   * On a mouse click print this page
  3.   */
  4. public void printPage()
  5. {
  6. // Hide the navigation buttons for the home page
  7. if ( wasMouseClicked() ) {
  8. Printout po = new Printout();
  9. po.showPageFormat();
  10. po.setJobName( "Survey Printout" );
  11. po.setHeader( "Left Header", Printout.LEFT );
  12. po.setHeader( "Right Header", Printout.RIGHT );
  13. po.setFooter( "Footer", Printout.LEFT );
  14. po.setPageNumbers( false, Printout.RIGHT, "Page " );
  15.  
  16. int numFrames = pageManager.getNumTargets();
  17. PrintableFrameSet pf = new PrintableFrameSet( po );
  18. for ( int i = 0; i < numFrames; i++ )
  19. pf.addFrame( pageManager.getTarget( i ) );
  20. po.addFrame( pf );
  21.  
  22. po.print();
  23. }
  24. }

The headers and footers are set so that their height is deducted from the printable area of the `page'. Headers and footers can be position to the LEFT , RIGHT or CENTER of the page.

Creating print forms

By default the printer is setup to print in Landscape format so as to match the aspect ratio of a typical screen. The printing framework automatically scales pages to the printer resolution so it is possible to create pages with different sizes or sizes more suited to the printer aspect ratio and resolution. Therefore custom pages could be setup for printing.

When using custom pages and adding them to the PrintOut you will need to take care of the initialization the page display framework would normally provide. This initialization includes updating layouts ( doLayout ), setting sizes, and updating bound components ( updateBoundComponentValues ) and perhaps even calling the page lifecycle methods ( pageCreated , pageActivated ).

Printing tables

As of version 2.0 Aria does not make use of the new JSE 5.0 printing support. Therefore if you wish to print tables you will need to handle sizing and pagination issues directly.

Other printout issues

While most of the time little by way of special consideration needs too be given to printing there are a few issues worth considering when preparing content.

Graphics

  1. Printing Content Issues

The resolution of printers is typically in thousands of dots per inch (dpi) whereas on-screen resolution is of the order of 72 dpi. An unscaled raster graphic that works well on screen will appear tiny on a printout and while Aria scales images it cannot (in most cases) increase the image resolution and so the printed quality of the image may suffer.

The solution to this problem is to use `lossy' raster graphics like JPEG that can interpolate the image or to use vector graphics like SVG that are ideal for scaling.

Tables

Tables frequently scroll or have varying amounts of content and this is not ideal for printing. At best a printout can accommodate vertical scrolling, but horizontal scrolling can rarely be accommodated well. When using tables pay special attention to testing with a variety of data.

Aspect

As has been noted above the resolution and aspect of the printed page normally differs from on-screen resolution. Consider using special print-only pages to accommodate your printouts. The print-only page can derive from the same class as the on-screen equivalent so any support functions and data binding can be reused.

Outputting to file

Another mechanism for printing is to use intermediate formats like HTML and Office file formats. An advantage of these formats is that the output can be of use for integration with other applications or within other documents and therefore you are not limited to the print format of your application.

Aria provides some utility classes to facilitate output to some of the more common formats. The utilities work best when some common elements of the output are grouped, thereby allowing reuse of code. The following example shows how a parts list can be generated in a number of formats. The example includes output that can be configured in several ways, exported to file or clipboard, and opened or printed:

First lets look at setting up the infrastructure. The output generation is facilitated by some exporters than do some of the most common formatting. Depending on the options chosen the appropriate exported is setup.

Setup the export infrastructure

  1. private void createExporter( String fileName ) throws Exception
  2. {
  3. if ( textRb.getState())
  4. exporter = new XExportHelper();
  5. else if ( wordRb.getState())
  6. exporter = new XExportHelper( ".doc" );
  7. else if ( xmlRb.getState() )
  8. exporter = new XmlExportHelper();
  9. else if ( htmlRb.getState() )
  10. exporter = new HtmlExportHelper();
  11. else if ( excelRb.getState() )
  12. exporter = new ExcelExportHelper( fileName );
  13. }

The createExporter method creates the processor for each type of export format that is supported. Each export format then has specific properties that must be set::

Setup the export format details

  1. private void exportText( Writer w )
  2. {
  3. try {
  4. exporter.setOutputWriter( w );
  5. exporter.setComponentFactory( pageHelper.componentFactory );
  6.  
  7. String delim = delimiterEdit.getText();
  8. exporter.setLeftDelimiter( delim );
  9. exporter.setRightDelimiter( delim );
  10. delim = separatorEdit.getText();
  11.  
  12. exporter.setFieldSeparator( delim );
  13. exporter.setFieldNameSeparator( delim );
  14. exporter.setUseWindowsLineEnd( windowsCheck.getState());
  15. doExport();
  16.  
  17. exporter.close();
  18. }
  19. catch ( IOException ex ) {
  20. }
  21. }
  22.  
  23. private void exportXml( Writer w )
  24. {
  25. try {
  26. exporter.setOutputWriter( w );
  27. exporter.setComponentFactory( pageHelper.componentFactory );
  28.  
  29. exporter.setUseWindowsLineEnd( windowsCheck.getState());
  30. doExport();
  31.  
  32. exporter.close();
  33. }
  34. catch ( IOException ex ) {
  35. }
  36. }
  37.  
  38. private void exportHtml( Writer w )
  39. {
  40. try {
  41. exporter.setOutputWriter( w );
  42. exporter.setComponentFactory( pageHelper.componentFactory );
  43.  
  44. exporter.setUseWindowsLineEnd( windowsCheck.getState());
  45. doExport();
  46.  
  47. exporter.close();
  48. }
  49. catch ( IOException ex ) {
  50. }
  51. }
  52.  
  53. private void exportExcel( String fileName )
  54. {
  55. try {
  56. exporter = new ExcelExportHelper( fileName );
  57. exporter.setComponentFactory( pageHelper.componentFactory );
  58.  
  59. exporter.setUseWindowsLineEnd( windowsCheck.getState() );
  60. doExport();
  61.  
  62. exporter.close();
  63. }
  64. catch ( IOException ex ) {
  65. ex.printStackTrace();
  66. }
  67. }

Most of this setup code is straightforward and involves setting up the export processor. The most involved is setting up the text export as a delimited file can vary by the type of delimiter and how the text values are separated. Some formats even have different left and right delimiters.

The actual export is initiated in the BeginExport method:

Start the export process

  1. public void beginExport()
  2. {
  3. String targetFile = locationEdit.getText();
  4. int type = TEXT;
  5. try {
  6. if ( wasMouseClicked() ) {
  7. if ( !clipboardCheck.getState() && ( targetFile.length() == 0 ) ) {
  8. showMessage( "Unable to Export", "You must enter a file name or click browse to choose a file!" );
  9. return;
  10. }
  11.  
  12. root = DataModel.getInstance();
  13.  
  14. Writer w = null;
  15. createExporter( targetFile );
  16.  
  17. // Choose the output, either the clipboard or an output stream (file)
  18. if ( !clipboardCheck.getState() && !excelRb.getState() )
  19. w = exporter.setupWriter( this, targetFile );
  20. else
  21. w = new StringWriter();
  22.  
  23.  
  24. if ( w != null ) {
  25. // Start the export process
  26. if ( textRb.getState() )
  27. exportText( w );
  28. else if ( xmlRb.getState() ) {
  29. exportXml( w );
  30. type = XML;
  31. }
  32. else if ( htmlRb.getState() ) {
  33. exportHtml( w );
  34. type = HTML;
  35. }
  36. else if ( excelRb.getState() ) {
  37. exportExcel( locationEdit.getText() );
  38. type = EXCEL;
  39. }
  40. else if ( wordRb.getState() ) {
  41. exportText( w );
  42. type = WORD;
  43. }
  44.  
  45. if ( clipboardCheck.getState() ) {
  46. exporter.setClipboardContents( ( ( StringWriter ) w ).getBuffer().toString() );
  47. }
  48. }
  49.  
  50. if ( exportRb.getState() || exportAndOpenRb.getState()) {
  51. showMessage( "Export complete", "Your file can be found at " + targetFile );
  52. if ( exportAndOpenRb.getState())
  53. doOpen( targetFile, false, type );
  54. }
  55. else if ( exportAndPrintRb.getState() ) {
  56. showMessage( "Export complete", "Your file has been printed and can be found at " + targetFile );
  57. doOpen( locationEdit.getText(), true, type );
  58. }
  59. }
  60. }
  61. catch ( Exception ex ) {
  62. ex.printStackTrace();
  63. showMessage( "Export failed", "Unable to export to " + targetFile );
  64. }
  65. }
  66.  

The export process involves setting up the output stream (or the clipboard) and calling the appropriate output method to take account of the output variations. The export process then finishes by writing the output and displaying some messages where appropriate.

If opening a file for saving the document then the following support methods helps access the file.

Open the output file

  1. /**
  2.   * Open or print a file
  3.   * @param fileName the file name
  4.   * @param doPrint true to print
  5.   */
  6. private void doOpen( String fileName, boolean doPrint, int fileType )
  7. {
  8. // This method is all over the palce and needs to be refactored!
  9. String command = "start";
  10. try {
  11. File tempFile = null;
  12. String tempFileName = null;
  13.  
  14.  
  15. if ( doPrint ) {
  16. switch ( fileType ) {
  17. case TEXT:
  18. command = "NOTEPAD /P" + "\"" + fileName + "\"";
  19. break;
  20.  
  21. case XML:
  22. case HTML:
  23. case EXCEL:
  24. case WORD:
  25. {
  26. if (( tempPrintFileName == null ) || ( fileType != lastFileType )) {
  27. tempFile = File.createTempFile( "doPrint", ".bat" );
  28. tempPrintFileName = tempFile.getCanonicalPath();
  29. lastFileType = fileType;
  30. tempOpenFileName = null;
  31. }
  32. tempFileName = tempPrintFileName;
  33.  
  34. if ( tempFile != null ) {
  35. OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) );
  36. writer.write( getOfficePath( fileType, doPrint ) + " %1 /mFilePrintDefault" );
  37. writer.flush();
  38. writer.close();
  39. }
  40. command = "start \"" + tempFileName + "\" \"" + fileName + "\""; ;
  41. }
  42. break;
  43. }
  44. }
  45.  
  46. else {
  47. if (( tempOpenFileName == null ) || ( lastFileType != fileType )) {
  48. tempFile = File.createTempFile( "doOpen", ".bat" );
  49. tempOpenFileName = tempFile.getCanonicalPath();
  50. lastFileType = fileType;
  51. tempPrintFileName = null;
  52. }
  53. tempFileName = tempOpenFileName;
  54.  
  55. if ( tempFile != null ) {
  56. OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) );
  57. writer.write( "start %1 %2 %3 %4 %5 %6 %7 %8 %9" );
  58. writer.flush();
  59. writer.close();
  60. }
  61. }
  62.  
  63. String line;
  64. String cmdLine;
  65. if ( doPrint )
  66. cmdLine = command;
  67. else
  68. cmdLine = "start \"" + tempFileName + "\" \"" + fileName + "\"";
  69.  
  70. File targetFile = new File( fileName );
  71. File startDir = new File( targetFile.getParent() );
  72. if ( BuildProperties.DEBUG )
  73. DebugLogger.log( "Running: " + cmdLine );
  74.  
  75. String osName = System.getProperty( "os.name" );
  76. String[] cmd = new String[ 3 ];
  77. if ( osName.equals( "Windows 95" ) || osName.equals( "Windows 98" ) ) {
  78. cmd[ 0 ] = "command.com";
  79. cmd[ 1 ] = "/C";
  80. cmd[ 2 ] = cmdLine;
  81. }
  82. else {
  83. cmd[ 0 ] = "cmd.exe";
  84. cmd[ 1 ] = "/C";
  85. cmd[ 2 ] = cmdLine;
  86. }
  87.  
  88. Process p = Runtime.getRuntime().exec( cmd );
  89. BufferedReader input = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
  90.  
  91. while ( ( line = input.readLine() ) != null ) {
  92. if ( BuildProperties.DEBUG )
  93. System.out.println( line );
  94. }
  95. input.close();
  96. }
  97. catch ( IOException ex ) {
  98. ex.printStackTrace();
  99. }
  100. }

An output file can be required for printing as the approach we take is to generate a temporary file and then invoke an application to print the document.

The opening of files in this way also requires that we build the command line appropriate for each application.

And when exporting to office it is necessary to find the office installation:

  1. private String getOfficePath( int fileType, boolean doPrint )
  2. {
  3. try {
  4. String applicationQuery = null;
  5. switch ( fileType )
  6. {
  7. case EXCEL:
  8. applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\EXCEL.EXE\\shell\\edit\\command";
  9. break;
  10. case HTML:
  11. if ( doPrint )
  12. applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command";
  13. else
  14. applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\iexplore.exe\\shell\\edit\\command";
  15. break;
  16. case TEXT:
  17. case WORD:
  18. default:
  19. applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command";
  20. break;
  21. }
  22. Process process = Runtime.getRuntime().exec( applicationQuery );
  23. StreamReader reader = new StreamReader( process.getInputStream() );
  24.  
  25. reader.start();
  26. process.waitFor();
  27. reader.join();
  28. String result = reader.getResult();
  29. int p = result.indexOf( REGSTR_TOKEN );
  30.  
  31. if ( p == -1 )
  32. return null;
  33. result = result.substring( p + REGSTR_TOKEN.length() ).trim();
  34. result = result.substring( 0, result.indexOf( "\"", 1 ) + 1 ).trim();
  35. return result;
  36. }
  37. catch ( Exception e ) {
  38. return null;
  39. }
  40. }

Ultimately, to generate the document we must ouput each part of the document

Run through each step of the document

  1. private void doExport() throws IOException
  2. {
  3. exporter.startDocument();
  4. exportPartsList();
  5. exporter.endDocument();
  6. }

And finally the elements within that document section must be output.

Run through each step of the document

  1. private void exportPartsList() throws IOException
  2. {
  3. exporter.writeSectionTitle( "Components" );
  4. exporter.setOutputFieldNames( false );
  5. exporter.startTable();
  6.  
  7. exporter.startHeader();
  8. exporter.writeField( "type", "" );
  9. exporter.writeField( "description", "" );
  10. exporter.writeField( "codeNo", "" );
  11. exporter.writeField( "quantity", "" );
  12. exporter.endHeader();
  13. int itemId = 0;
  14. int numNodes = getNumNodes();
  15. for ( int i = 0; i < numNodes; i++ ) {
  16. DataModel targetNode = getNode( i );
  17. if ( targetNode != null ) {
  18. exporter.startGroup();
  19. exporter.startRecord();
  20. exporter.startElement( "component" );
  21. exporter.writeField( "type", targetNode.getAttribValueAsString( BaseModel.VALUE_ATTRIBUTE ));
  22. exporter.writeField( "description",
  23. pageHelper.componentFactory.translate(
  24. (String)targetNode.get( "@Description" ) ));
  25. exporter.writeField( "codeNo", ( String ) selectedNode.get( "@Code_no" ));
  26. exporter.writeField( "quantity", "1" );
  27. exporter.endRecord();
  28. int accessories = 0;
  29. for ( int ij = 0; ij < accessoryList.length; ij++ ) {
  30. Object accessoryCode = selectedNode.get( "@" + accessoryList[ ij ] );
  31.  
  32. if ( accessoryCode != null ) {
  33. exporter.startRecord();
  34. if ( accessories == 0 )
  35. exporter.endElement();
  36. exporter.startElement( "accessory" );
  37. exporter.writeField( "type", "" );
  38. exporter.writeField( "description",
  39. pageHelper.componentFactory.translate(
  40. Functional.accessoryDescription[ ij ] ));
  41. exporter.writeField( "codeNo", (String)accessoryCode );
  42. exporter.writeField( "quantity",
  43. new Integer( Functional.accessoryQuantity[ ij ] ).toString() );
  44. accessories++;
  45. exporter.closeElement();
  46. exporter.endRecord();
  47. }
  48. }
  49. if ( accessories == 0 )
  50. exporter.closeElement();
  51. else
  52. exporter.matchElement();
  53. exporter.endGroup();
  54. }
  55. }
  56. }
  57. }
  58.  
  59. exporter.endTable();
  60. exporter.setOutputFieldNames( true );
  61. exporter.writeSectionEnd();
  62. exporter.writeBlankLine();
  63. }

This last output method is very much application specific, but the example above groups some items (the accessories) with a main item.

To create a print out with this mechanism requires a more work than printing directly but it offers more flexibility. Furthermore the content of an Excel sheet can be alot more powerful than a flat printout.

Printing via Excel

Aria includes a set of wrapper classes to ease the use of Excel sheets. While Excel worksheets have many potential uses they can be used for printing purposes with the sheet being created directly or with a pre-prepared sheet acting as a template. Aria also includes classes to allow you easily launch Excel to preview the `printout' prior to actual printing.

Using the code above we can get the following Excel sheet (displayed in OpenOffice 2.0):

Printing and interacting with OpenOffice

OpenOffice can be used in much the same way as Excel however OpenOffice provides some extra programming control and support that can be useful in some situations.

OpenOffice can be run in a server mode where the worksheet is generated or populated by a server. This opens up the possibility of doing some sorts of workflow.

Printing via HTML

HTML provides another option for printing. Aria includes support for printing things such as forms and tables to HTML. However because of the wide variety of formats that could be generated you should be prepared to manually code some of the HTML generation.

Again using the code above we can get the following HTML, shown in the Mozilla FireFox browser:

Importing Forms

Aria allows XML documents and forms to be imported and rendered as pages or forms within an application. The imported document can make use of all of Aria and Aria's facilities.

The form import relies on a mapping of types or XML elements and once this mapping has been setup the application can import just about any document that uses the types described in the mapping.

Page Builders

Aria employs an extensible method of loading pages. The page manager normally loads pages from XML or from Java classes if it cannot find a suitable XML page. This process can be altered by setting up secondary page loaders. The page loader is described by the PageLoader interface and can be configured through the startup property value for BuilderClass and NumBuilderClasses. Aria includes several page loaders including Aria's default page loader, an HTML page loader (see Importing HTML.) and a generic page loader for loading forms and other file formats (see Importing forms.).

Using this builder mechanism Aria can handle a wide variety of file formats through the addition of additional builder classes.

The Generic Builder

Aria also includes a menchanism for generically handling different XML file formats. The GenericBuilder is driven with an XML configuration file that describes how various elements in the XML are to be treated. Multiple builders can be installed for different file types and the builders are tested in the order in which they are registered to see if they can load a particular file. The file extension may be used to determine the file type, but this is not always necessary.

The generic builder takes an XML configuration file GenericBuilderConfig named in the startup properties file. Check the builder's documentation for more details as the individual builders may customize this behavior.

Generic Builder Configuration

The GenericBuilder 's configuration file is divided into several sections: components , instructions and attributes .

Component Mapping

The components section defines how XML elements in the source or incoming file format may be transformed to Aria components. The mapping can be one to many, many to one or many to many, so there can be extensive rearrangement and restructuring of the information. The mapping specifies the types of components to be instantiated and the way the attributes are then applied to the new component. For example in the following mapping

Mapping a set element to two Aria components

  1. <Composite id="Set">
  2. <Component type="Label" name="caption"/>
  3. <Component type="List" name="input"/>
  4. <Attributes>
  5. <Attribute srcAttrib="name" destAttrib="name" component="input"/>
  6. <Attribute srcAttrib="title" destAttrib="content" component="caption"/>
  7. <Attribute destAttrib="alignment" value="right" component="caption"/>
  8. </Attributes>
  9. </Composite>

the Set element is rendered as a XLabel and a XList component. Each of the new components is named in the mapping so that the attributes of the Set element can be applied to the two new components depending upon which of the named components is referenced in the Attribute element of the mapping.

Some formats may map an element type to a range of types on the basis of some secondary attributes. The mapping file can specify these attributes with the matchAttrib and matchValue attributes, where the element is matched on the basis of the attribute (or comma separated list of attributes). The component mapping then checks that the source file specifies values for these attributes that match the values specified in the mapping's matchValue attribute (again this can be a comma separated list). When multiple mappings are specified in this way the mappings are searched in the order in which they are specified and therefore it is important to list the mappings so that the most specific mappings are listed first. The last mapping should then be the most generic or default mapping.

When multiple mappings are specified the default mapping may also be specified by including a match value of ` * '.

Attribute Mapping

A file format may use a common attribute across multiple components and rather than having to specify this attributed repeatedly for each component type a common attribute mapping can be specified. For example the following specification:

A common attribute specification

  1. <Attributes>
  2. <Attribute srcAttrib="style"
  3. destAttrib="${this.setStyle(${comp},${value})}"
  4. component="caption"/>
  5. </Attributes>

of a style attribute. In the above example the setStyle method is invoked with the component named by the component attribute and the value of the attribute specified in the original element.

If the destAtrrib is missing it is assumed to be the same as the srcAttrib , equally the component name is assumed to be main if unspecified.

Only attributes that are specified are passsed to the component and therefore it is at leaset necessary to name the attributes that are used.

XMultiAttributedComponent interface extends XAttributedComponent and adds the methods endMapping which is called when all the attributes have been set. The purpose of the interface is to allow a component to process the attributes when all are available. The use of allAttributesSet requires that the component stores the attributes (they are stored by the page anyhow, and can be retrieved with the getAttribute method). Compound attributes such as the styles can be set in the way even if the style values are specified separately.

Mapping Instructions

The source XML may contain elements that do not map to components and these elements can be handled by processing instructions.

Extra processing instructions

  1. <Instructions>
  2. <Processor id="AlertProcessor"
  3. class="org.formaria.oracle.forms.pi.AlertProcessor"/>
  4. <Instruction id="Spacer">
  5. <Action method="${addSpacer()}"/>
  6. </Instruction>
  7. <Instruction id="RowSpacer">
  8. <Action method="${addRowSpacer()}"/>
  9. </Instruction>
  10. </Instructions>

In the above example the processing instructions specify calls to the addSpacer or addRowSpacer method when Spacer or RowSpacer element are encountered. The builder class may provide some useful methods or the evaluated attribute can reference library methods. In addition to the normal set of library method references the instruction processing may specify an additional instruction processors (each of which implement the XInstructionProcessor interface).

Layering in the rendered form

Since content generated on the fly may exhibit some aspects of a lowest common denominator approach the generic builder allows composition of content through layering. Each layer can be specified separately and tis process makes it possible be combined predefined content (normally in background layers) and dynamically generated content. The predefined content may contain artwork and branding related content such as logos, or the layers may contain common elements such as navigation items and so forth.

Opening HTML within the IDE

A limited form of HTML may be edited within the Aria editor. For this to work the first element in the html must be XHtmlPage .

Importing HTML

Aria allows HTML files to be opened and saved as Aria xml files. This import facility provides a means of working with existing web applications and enabling an upgrade to Aria and Aria.

One size doesn't fit all and while Aria and Aria applications may not be for everyone, it is equally true that vanilla HTML applications, or even Ajax enabled web applications are not adequate for all situations. Where web applications are insufficient you may want to provide an upgrade path for power users or users who's requirements differ from the norm.

Aria and Aria already support multiple widget sets, so they can be used to target a wide variety of platforms. The HTML widget support discussed earlier is one means of supporting lightweight clients via web browsers, but it is still reliant on the web browser and is therefore limit to what that browser can do. Furthermore the security and compatibility issues faced by the browser may not be acceptable by everyone.

To address these situations and to provide a means of processing HTML Forms, Aria has the capacityto import HTML pages and save them as Aria XML files. Once imported the pages can be manipulates as any other Aria page.

Opening HTML within the IDE

The html import facility within the editor is enabled by default and all you need do is locate and open an HTML file within the files view. As the file is opened it is parsed and a Aria representation is built.

Not that the W3C HTML 4.0 standard is used when processing the HTML and therefore not everything that can be rendered in the latest web browsers will be importable.

Once the file has been imported and displayed you can begin to edit the files. (When you save the file a new file with the .xml extension is created instead of overwriting the original html file). Because of the layout limitations inherent in HTML many HTML pages will have redundant element and redundant objects for formatting and you may want to remove as much of this redundancy as possible, however if you anticipate tracking updates to the original HTML files you may not wish to make such changes.

During the import process the JavaScript contained in the HTML is imported and method stubs are created for the event handlers that will be needed to replicate the HTML's behavior. A comment containing the original JavaScript code is added within the stub of each event handler.

Using the HtmlBuilder

In fact the import facility is not restricted to the IDE, it can be used at any point within Aria by adding a BuilderClass property to the startup properties file:

Setting up the HtmlBuilder

  1. BuilderClass=org.formaria.builder.w3c.html.HtmlBuilder</P>
  2. </TD>
  3. </TR>
  4. <TR>
  5. <TD >
  6. <P>
  7. NumBuilderClasses=2
  8. BuilderClass0=org.formaria.builder.AriaBuilder
  9. BuilderClass1=org.formaria.builder.w3c.html.HtmlBuilder

Customizing the HtmlBuilder

The HtmlBuilder is designed to be customizable, right down to the level of processing individual HTML tags. The builder contains a table of tag handlers, each of which is a prototype for the handler that processes the various element types recognized by the html parser. Therefore, by replacing the prototype instance with a custom one you can add custom processing.

POJOs

As we have seen earlier Aria supports a number of ways of working with POJOs, Hibernate and other data structures either within the editor using drag and drop or through the use of customized builders. Aria also supports the dynamic creation of forms as runtime with little or no special configuration

Introducing PojoPanel

PojoPanel is a panel that takes a reference to a POJO and constructs a user interface for that component based upon the POJO's public properties and methods. The panel configures the data bindings and event handlers for the UI automatically. In declaring an instance of the panel it is possible to provide layout hints for things like the number of columns, spacing, field order, exclusion of fields and more.

The PojoPanel can be used with POJOs from both Hibernate and Spring as well as other sources.

For example the following code binds to a POJO:

A sample page using a PojoPanel

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Page w="1016" h="734" class="com.myproject.MyController">
  3. <Components>
  4. <Panel title="My Form" border="0" layout="border">
  5. <PojoPanel
  6. name="myForm"
  7. constraint="center"
  8. style="base"
  9. pad="6"
  10. spacing="4"
  11. numCols="2"
  12. dataPath="pojo/myModel/myDataObject"
  13. methods="save"
  14. class="com.myproject.transfer.MyDataObject"
  15. fieldOrder="a,c,d,b,f"/>
  16. <Panel constraint="south" layout="flow" align="center" style="base"
  17. hgap="6" vgap="6">
  18. <Button name="saveBtn" content="Save"/>
  19. </Panel>
  20. </Panel>
  21. </Components>
  22. <Events>
  23. <Event target="saveBtn"
  24. method="${pojo/myModel.saveStuff(pojo/model/myDataObject)}"
  25. type="org.formaria.pojo.SaveBeforeActionHandler"/>
  26. </Events>
  27. </Page>

where the page is setup as normal and the class com.myproject.MyController is the normal Page implementation. The key component is the PojoPanel declaration. Here the fields have the following roles:

PojoPanel attributes

Attribute

Role

pad

gives the spacing around the content/columnar data

spacing

sets the spacing between the items that are placed on the panel

numCols

sets the number of columns (labels and input) used by the panel

dataPath

refers to the object instance that is used as the source for the data bindings

class

the class whose fields and methods are used in the panel

methods

corresponds to the names of the methods on the class referred to by the class attribute that are linked to the panel

fieldOrder

an override for the order in which the fields are displayed

fieldValidationRule

is the name of the validation rule for the input fields

excludes

(not shown above) a list of fields not to include in the display

The event handling follows a similar rationale with the method being declared as an evaluated attribute. Aria 4.0 introduces new evaluated attribute support whereby the data model nodes can be referenced and the methods of those objects invoked. In the above setup the saveStuff method of the data model object at the path pojo/myModel is invoked. The method takes one argument and again this is a data model object - in the example it corresponds to the POJO used by the PojoPanel. The event adapter SaveBeforeActionHandler simply calls saveBoundComponentValues beofre the POJO's method is invoked.

In populating the panel the PojoPanel inspects the POJO looking for properties (as defined by the Java Bean specification) and methods (like the save method above). If a method with just a getter is found then it is assumed to be read-only.

When a property is found the PojoPanel sets up the data bindings to the appropriate POJO getter and setter methods. In addition the PojoPanel checks to see if a vocabulary has been defined for the field by checking for the existence of a model node at vocab/<<[field name]>>. If such a vocabulary is found it is assumed that the field value is part of a pick list and a drop down list/combo is displayed for that component. The PojoPanel also inspects the type of each property and tries to assign input fields of the appropriate type, for example a DateEdit input is created for a date property.

Generally the field or property name is not appropriate for display as a label or prompt and therefore the PojoPanel looks up the language files for a translation of the property name.

The above example is rendered as:

Later we will add an additional configuration file to control how the panel is rendered. The configuration will be based upon the options available within the Aria editor when creating panels from POJOs with drag and drop.

Building the Panel

In populating the panel the PojoPanel inspects the POJO looking for properties (as defined by the Java Bean specification) and methods (like the save method above). If a method with just a getter is found then it is assumed to be read-only.

Customizing the View

In some case not all the POJO fields will be required for a form, or it may be desireble to present the POJO in different ways to different users and therefore it is possible to customize the behavior with a view. The view can be loaded in two ways, either through and XML configuration file or through a data structure held within the data model.

The file structure used by the view is as follows:

Basic view file format

  1. <View>
  2. <Properties enabledByDefault="true" includeByDefault="false"
  3. fieldValidationRule="pojoFields"/>
  4. <Field name="fieldname1" visible="true" enable="true" exclude="false" type="Edit" />
  5. <Field name="fieldname2" visible="true" enable="true" exclude="false" type="Edit" />
  6. ...
  7. <Field name="fieldnameN" visible="true" enable="true" exclude="false" type="Edit" />
  8. </View>

The fields are declared in the order in which they should appear on the form and the Field element can include an arbitrary number of attributes.

Alternatively the data can be stored in an XmlElement within the data model. When stored in this way an adapter can be used to construct the data structure, adapting the field values as needed, for example

Adapting a model element to the internal view format

  1. public class ViewAdapter
  2. {
  3. public static XmlElement adapt( List<ViewDTOElement> view )
  4. {
  5. Hashtable<String,XmlElement> viewFields = new Hashtable<String,XmlElement>();
  6. int numFields = view.size();
  7.  
  8. NanoXmlElement parentNode = new NanoXmlElement( "View" );
  9. NanoXmlElement[] children = new NanoXmlElement[ numFields ];
  10.  
  11. int idx = 0;
  12. for ( ViewDTOElement entry : view ) {
  13. nanoXmlElement element = new NanoXmlElement( "field" );
  14. children[ idx ] = element;
  15. element.setAttribute( "name", entry.getId());
  16. String visibility = entry.getEvisibability();
  17. boolean enabled = !"readonly".equalsIgnoreCase( visibility );
  18. boolean visible = "editable".equalsIgnoreCase( visibility ) ||
  19. !enabled;
  20. element.setAttribute( "visible", Boolean.toString( visible ));
  21. element.setAttribute( "type", entry.getEtype() );
  22.  
  23. element.setAttribute( "label", entry.getId() );
  24. element.setAttribute( "enabled", Boolean.toString( enabled ));
  25. element.setAttribute( "exclude",
  26. Boolean.toString( "private".equalsIgnoreCase( visibility )));
  27. element.setAttribute( "order", Integer.toString( idx++ ));
  28. }
  29. for ( NanoXmlElement child : children )
  30. parentNode.addChild( child );
  31.  
  32. return parentNode;
  33. }
  34. }

PojoPanels and Dialogs

PojoPanels can be nested within dialogs using the PojoDialog class. For example:

Create a dialog from the Generic PojoDialog

  1. EditAccountDetails accDetailsDialog =
  2. (EditAccountDetails)PojoDialog.createDialog(
  3. EditAccountDetails.class,
  4. PojoDialog.READ_ACTION,
  5. "pojo/currentAccountDTO",
  6. "Account details" );
  7.  
  8. accDetailsDialog.setLogo( "hands.gif" );
  9. accDetailsDialog.setViewFile( "views/accounts" );
  10. accDetailsDialog.showDialog( this );
  11.  
  12. int status = Dialog.getLastReturnValue();
  13. if ( status == Dialog.OK_CLICKED ) {
  14. ...
  15. }

The above creates a dialog for the pojo, nesting a panel within the dialog and providing some basic CRUD operation support. The dialog uses a PojoDialogTemplate.xml file so that the appearance of the dialog can be fine tuned for individual applications.

Data Binding

When a property is found the PojoPanel sets up the data bindings to the appropriate POJO getter and setter methods. In addition the PojoPanel checks to see if a vocabulary has been defined for the field by checking for the existence of a model node at vocab/<<[field name]>>. If such a vocabulary is found it is assumed that the field value is part of a pick list and a drop down list/combo is displayed for that component. The PojoPanel also inspects the type of each property and tries to assign input fields of the appropriate type, for example a DateEdit input is created for a date property.

The alwaysDirty attribute controls how the paths are evaluated. Normally the PojoModel nodes cache their data and do not invoke the POJO method each time they are accessed. If you are trying to binding to a value that might change, such as a current user or a details node in a master-detail relationship then the underlying object might change and therefore the cache becomes out of sync. Setting the alwaysDirty attribute means that the POJO method is always evaluated and it should therefore return the correct value in such a case.

Event Handling

The event handling follows a similar rationale with the method being declared as an evaluated attribute. Aria 4.0 introduces new evaluated attribute support whereby the data model nodes can be referenced and the methods of those objects invoked. In the above setup the saveStuff method of the data model object at the path pojo/myModel is invoked. The method takes one argument and again this is a data model object - in the example it corresponds to the POJO used by the PojoPanel. The event adapter SaveBeforeActionHandler simply calls saveBoundComponentValues beofre the POJO's method is invoked.

Prior to Aria 4.0 it was not possible to access the methods of a POJO via Aria's event handling and this meant that the page class had to contain a proxy method to invoke method on the POJO. As of version 4.0 POJO methods can be invoked by specifying them as any other event

Adding a POJO event handler

  1. <Events>
  2. <Event target="saveBtn"
  3. method="${pojo/myModel.saveStuff(pojo/model/myDataObject)}"
  4. type="org.formaria.pojo.SaveBeforeActionHandler"/>
  5. </Events>

when the event handler finds a slash (/) in the method specification it assumes it is dealing with a model node. Using this approach the path pojo/myModel is assumed to be a model node wrapping a POJO which contains the method saveStuff. The method may take arguments which in turn can point to model nodes. In the above example the argument pojo/model/myDataObject is also assumed to be a model node.

As the event handling declarations are processed when the page is loaded the event handler stores a reference to the model model implementing the method and also the model nodes providing the arguments.

During the life of an application the content of the model nodes may change and therefore the event handler does not normally get the content of the node till it needs to invoke the target method. However during setup the event handler gets the POJO wrapped by the model node so as to determine the class and locate the method to be used in handling the event. Therefore the object/POJO contained within the model node should remain consistent throughout the life of the application, or at least whenever the event handler is invoked.

If you want to fix the POJO used by the event handler then prefix the path with a / to make it an absolute node reference. In such a case the POJO is accessed immediately that the event handler declaration is processed and the POJO reference is accessed and retained.

Localization

Generally the field or property name is not appropriate for display as a label or prompt and therefore the PojoPanel looks up the language files for a translation of the property name.

Validation

The input fields can be validated on a field by field basis, or for the POJO as whole. If the # fieldValidation attribute of the panel is defined then a field validation is added for each input field and the user input is validated when the input loses focus. If the appropriate styles are configured then the background colour for the inputs will reflect the validation status of the fields.

If a validation rule is specified for the panel as a validation element then the POJO is validated as a whole, that is all fields are validated. It is possible to have both styles or validations or one or the other.

The validation rule used for POJOs should map to a validation that implements the PojoValidator interface. The interface provides two methods

POJO Validation interface

  1. /**
  2.   * Validate the pojo
  3.   * @param obj
  4.   * @return the validation result
  5.   */
  6. public Object validate( Object obj );
  7.  
  8. /**
  9.   * Validate a field/property of the pojo
  10.   * @param obj
  11.   * @param fieldName
  12.   * @return the validation result
  13.   */
  14. public Object validate( Object obj, String fieldName );

The PojoValidator is a basic implementation of this interface. Using this object is possible to wrap third party validation frameworks, as during the validator setup the PojoValidator instance is passed the dataPath attribute, and using the attribute value the validator looks up the POJO upon which it will operate prior to performing the validations.

Spring support

Leveraging the POJO support and runtime form generation enables Aria to used effectively for Spring applications. Spring's remoting technology can expose beans to a remote client and Aria can pick these beans up and create forms and tables for the beans. The PojoPanel described above makes it possible to quickly and easily configure the UI for these beans with little or no coding.

Going one step further it is also possible to provide hints to the panel generation to control various aspects of the UI including styling other attributes of the appearance and behavior. At the core of the support for Spring is the use of reflection and bean inspectors to determine the field types and bean methods. However additional glue logic is provided on the basis that the beans and their attributes are named and handled consistently. Therefore a bean can be bound to UI component and validated against other Spring beans provided that this use of property names is consistent.

With so much provided by Aria creating a Spring based application requires little other than wiring the beans together. The most important requirement in this process is to wire the Spring beans into Aria's data model. Normally an application will have some class of root access point, service facade or some object that ties the various beans together into a session or context of some sort. Aria needs this object to be mapped into the Aria data model, and this can be done as follows:

Setting up Spring support

  1. public class MyPojoRoot extends PojoContext implements PojoValidator
  2. {
  3. ...
  4.  
  5. /**
  6.   * Instantiated by the POJO bindings at startup.
  7.   */
  8. public MyPojoRoot()
  9. {
  10. try {
  11. applicationContext = new ClassPathXmlApplicationContext( new String[] {
  12. "spring-config-file-A.xml",
  13. "spring-config-file-B.xml" }
  14. );
  15.  
  16. serviceInterface = (SomeBean)applicationContext.getBean( "someBean" );
  17. }
  18. catch ( BeansException be ) {
  19. be.printStackTrace();
  20. }
  21. }
  22.  
  23. public static MyPojoRoot getInstance( Project project )
  24. {
  25. MyPojoRoot rootPojo = (MyPojoRoot)project.getObject( "MyPojoRoot" );
  26. if ( rootPojo == null )
  27. rootPojo = new MyPojoRoot();
  28.  
  29. rootPojo.setProject( project );
  30.  
  31. return rootPojo;
  32. }
  33.  
  34. public Object getRoot()
  35. {
  36. return getInstance( project );
  37. }
  38.  
  39. public void setProject( Project p )
  40. {
  41. project = p;
  42. pageMgr = project.getPageManager();
  43. project.setObject( "MyPojoRoot", this );
  44. }
  45.  
  46. public void configure( URL configFileURL )
  47. {
  48. rootModel = project.getModel();
  49. }
  50.  
  51. public void modelLoaded()
  52. {
  53. }
  54.  
  55. /**
  56.   * Lookup some other bean
  57.   */
  58. public IOtherBean getOther()
  59. {
  60. if ( other == null )
  61. other = (IOtherBean)serviceInterface.getOtherBean();
  62.  
  63. return other;
  64. }
  65.  
  66. //-Validation----------------------------------------------------------------
  67. /**
  68.   * Validate the pojo
  69.   * @param obj
  70.   * @return the validation result
  71.   */
  72. public Object validate( Object obj )
  73. {
  74. return serviceInterface.validate( obj );
  75. }
  76.  
  77. /**
  78.   * Validate a field/property of the pojo
  79.   * @param obj
  80.   * @param fieldName
  81.   * @return the validation result
  82.   */
  83. public Object validate( Object obj, String fieldName )
  84. {
  85. return serviceInterface.validate( obj, fieldName );
  86. }
  87. }

The above code also handles the interface to the validation of POJOs.

Once the root of the POJO model has been added it may be convenient to wrap the root POJO with some additional access functions so that various nodes can be more eassily accessed. For example an application often uses the concept of a current node or current object and this may not be provided explicitly by the POJOs. The root node or model nodes hanging of the root might be a good point to wrap the POJOs and add additional functionality. The root node is also a good point to load Spring's configuration files

Packaging and Deployment

This chapter will describe the steps involved in packaging and deploying a Aria application. It will cover topics such as running from the command line, from a website using Java webstart, creating native executables and installation wizards.

Package your project

For convenience, it can be useful to create a batch file which will package all of the resource and class files and install them into a single jar file. So, by creating the following batch file in the root of a Aria project all of the project files can be merged into a single jar.

Sample jar script

  1. # Create the file with a mainfest file
  2. c:\j2sdk1.4.2_03\bin\jar cvf bundle.jar META-INF/MANIFEST.MF
  3. # navigate to the resources directory and jar it's contents.
  4. # Repeat this for all resource files
  5. cd resources
  6. c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.*
  7. cd ..\pages
  8. c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.*
  9. cd ..\images
  10. c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.*
  11. cd ..\classes
  12. c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.*
  13. # Extract the contents of the jars which need to be included
  14. # in the application into a tmep directory and jar them.
  15. cd ..\lib
  16. rd temp
  17. md temp
  18. cd temp
  19. ########## STARTED EXTRACTING JARS ############
  20. c:\j2sdk1.4.2_03\bin\jar xvf ../AriaRuntimeCommon.jar
  21. ########## FINISHED EXTRACTING JARS ###########
  22. c:\j2sdk1.4.2_03\bin\jar uvf ../../bundle.jar *.*
  23. cd..\..

The first line in this script references a file called META-INF/MANIFEST.MF which looks like...

The manifest file

  1. Manifest-Version: 1.0
  2. Main-Class: org.formaria.swing.Applet

This file simply tells the jar that the org.formaria.swing.Applet class needs to be loaded when the jar file is run. Note that the startup file is not specified in which case the Applet class looks for and uses the startup.properties file if it is present.

You should now be able to run the application simply by double-clicking the jar file.

Running a Aria application from the command line

Again, it is easier to create a batch file to launch the application. So, by creating a file ` run.bat ' in the root of the Aria project folder, the application can be launched using the jar which was created in the previous section

Launch Aria with the command line

  1. java.exe -cp bundle.jar org.formaria.swing.Applet startup.properties

The startup.properties parameter is not necessary if the startup file is called ` startup.properties '.

If you do not wish to bundle all of your jars into a single jar you may wish to create the following file to launch the application.

Specify the jars separately

  1. java -cp .;.\lib\AriaRuntimeCommon.jar;.\bundle.jar;
  2. org.formaria.swing.Applet startup.properties

In this case you will want to modify the bundle.bat file so as to remove the commands which jar the contents of the dependancies. The following lines should be removed from the bundle.bat file.

Lines to be remove from the bundle.bat file

  1. # cd ..\lib
  2. # rd temp
  3. # md temp
  4. # cd temp
  5. ########## STARTED EXTRACTING JARS ############
  6. # c:\j2sdk1.4.2_03\bin\jar xvf ../AriaRuntimeCommon.jar
  7. ########## FINISHED EXTRACTING JARS ###########
  8. # c:\j2sdk1.4.2_03\bin\jar uvf ../../bundle.jar *.*
  9.  

Running a Aria application using Java Web Start

Java Web Start is used where applications need to be deployed over the internet but do not run within the confines of a web browser. The client machine which attempts to run the application must have a valid Java runtime installed

Java Web Start reads the contents of a JNLP file which resides on a webserver in order to initialise an application. Before the application can be used, however, the jar file which is to be used needs to be signed with a code signing digital certificate. This certificate will be registered to your company name and can be purchased from one of the following.

  1. Sources for code signing certificates

Company

URL

Verisign

http://www.verisign.com/products-services/security-services/code-signing/digital-ids-code-signing/

Thawte

http://www.thawte.com/codesign/

GlobalSign

http://www.globalsign.net/digital_certificate/objectsign/index.cfm

Alternatively you can create your own test certificate using the tools supplied with the JDK (the keytool tool with the -selfcert option). Once the certificate has been created you need to import it into your keystore, if you don't already have a keystore then you will need to create one.

Generate a new keystore for you certificates

  1. c:\jdk1.5.0\bin\keytool
  2. -genkey
  3. -alias my_keystore
  4. -keypass mykeypassword
  5. -keyalg RSA
  6. -keystore mycompany.keystore
  7. -storepass mystorepassword

The new keystore will be created in the current working directory. Next, to start the process of signing you need to generate a certificate signing request:

Generate a certificate signing request

  1. c:\jdk1.5.0\bin\keytool
  2. -certreq
  3. -alias my_keystore
  4. -keystore mycompany.keystore
  5. -file certreq.txt

The output file certreq.txt needs to be sent to the signing authority by cutting and pasting the content into the forms provided on the authorities website (the process may differ from company to company). Using the new certificate the jar file can be signed using the JDK. When the process is complete you should then receive a new certificate from the signing authority. The new certificate needs to be imported into your keystore:

Import the new certificate

  1. c:\jdk1.5.0\bin\keytool -alias my_keystore -keypass mykeypassword -keystore mycompany.keystore -storepass mystorepassword -import -file cert.txt -trustcacerts

The keystore is now ready for use and you can sign the bundle.jar file using the following command, so that a new version of the jar, bundleS.jar , is output.

Signing the jar file

  1. jarsigner
  2. -keystore mycompany.keystore
  3. -signedjar bundleS.jar bundle.jar

The signed jar can now be deployed to a webserver along with a JNLP file. For the new bundleS.jar file the JNLP file might look like...

A sample JNLP file

  1. <jnlp spec="1.0+" codebase="http://www.mydomain.com/demo/">
  2. <information>
  3. <title>Aria Application</title>
  4. <vendor>My Company Ltd</vendor>
  5. <homepage href="http://www.mydomain.com/demos" />
  6. <description kind="">Some application</description>
  7. <description kind="tooltip">Aria Application</description>
  8. <offline-allowed />
  9. </information>
  10. <resources>
  11. <j2se version="1.4+" />
  12. <jar href="lib/AriaRuntimeCommonS.jar" main="true" download="eager"/>
  13. <jar href="lib/bundleS.jar"/>
  14. </resources>
  15. <application-desc main-class="org.formaria.swing.Applet">
  16. <argument>startup.properties</argument>
  17. </application-desc>
  18. <security>
  19. <all-permissions />
  20. </security>
  21. </jnlp>

In this example the JNLP file can be invoked by referencing http://www.mydomain.com/demos/aria.jnlp

The .jnlp extension needs to be included in the mime types for the webserver it is running on if not already done so. The method of doing this varies accross webservers so the documentation for the webserver should be referred to.

Note that in the above example the Aria jars are also listed, but you could just include everything in the one jar file. However, leaving the Aria code in separate jars means that you can reuse the jars in other applications and if the same URL is used then the jars will not need to be downloaded again by the user's system. Also note that all the jars need to be signed by the same certifcate (i.e. your certificate) and therefore the Aria jars are not supplied in a signed form. Finally, the main="true" attribute tells JWS that the jar contains the entry point, or main method, which is used to start the application.

NetBeans 5.0 supports an add-on module to edit JNLP files. Check the NetBeans Update Center for details.

Additional Java Web Start documentation

Resource

URL

JWS Developer's guide

/docs/guide/javaws/developersguide/contents.html

Signing tools

/docs/tooldocs/index.html#security

Java deployment guide

/docs/guide/deployment/deployment-guide/contents.html

JWS examples

/docs/relnotes/samples.html

NetBeans JWS plug-in

http://www.netbeans.org/kb/articles/matisse-jaws.html

JNLP visual editor

http://public.wuapaa.com/public/NetBeans/

Setting up a JWS extension

If you have more than one application based upon Aria you may wish to share the core jars between applications, so that they are not repeatedly downloaded for each application. The JNLP specification allows resources to be specified as an extension, and the extension can contain the common jars (with the restriction that the jar containing the main method must be referenced in the primary JNLP file).

A sample JNLP file

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <jnlp spec="1.0+" codebase="http://www.mydomain.com/demo/">
  3. <information>
  4. <title>Aria Application</title>
  5. <vendor>My Company Ltd</vendor>
  6. <homepage href="http://www.mydomain.com/demos" />
  7. <description kind="">Some application</description>
  8. <description kind="tooltip">Aria Application</description>
  9. <offline-allowed />
  10. </information>
  11. <resources>
  12. <j2se version="1.4+" />
  13. <jar href="lib/AriaRuntimeCommonS.jar" main="true" download="eager"/>
  14. <extension name="Aria" href="aria.jnlp"/>
  15. <jar href="lib/bundleS.jar"/>
  16. </resources>
  17. <application-desc main-class="org.formaria.swing.Applet">
  18. <argument>startup.properties</argument>
  19. </application-desc>
  20. <security>
  21. <all-permissions />
  22. </security>
  23. </jnlp>
  24.  
  25. <?xml version="1.0" encoding="utf-8"?>
  26. <jnlp spec="1.0+" codebase="http://www.mydomain.com/demos/" href="aria.jnlp">
  27. <information>
  28. <title>Aria</title>
  29. <vendor>Formaria Ltd.</vendor>
  30. <homepage href="http://www.mydomain.com/demos"/>
  31. <offline-allowed/>
  32. </information>
  33. <resources>
  34. <jar href="lib/AriaRuntimeCommon.jar" main="false"/>
  35. <jar href="lib/jxl.jar"/>
  36. </resources>
  37. <component-desc/>
  38. </jnlp>

Deploying a webstart application via CD

Once you have created and deployed a JWS applicationit is possible to put that application on CD so that the features of JWS are available to anyone who has installed from CD. The result is that you can distribute your application via CD or other routes, install that application onto the desktop and into the JWS cache so that the application will run in an off-line mode and so that when on-line it will check for updates.

To install into the JWS cache it is necessary to import the jar files. Assuming you have copied your server's version of the application jnlp and jar files, you then need to create an additional file ( install.bat or install.cmd on Windows):

Import the jars into the JWS cache

  1. @echo off
  2. SET DRIVE=%0
  3. SET DRIVE=%DRIVE:&#126;1,1%
  4. c:\jdk1.5.0\bin\javaws
  5. -codebase file:///%DRIVE%:/myapp/
  6. -import %DRIVE%:/myapp/myApp.jnlp

Once this command file is run the files will be imported into the JWS cache and depending on the content of the JNLP file, desktop and menu shortcuts may be setup.

You will probably want to add an auto install option to your CD, and if so, you should use the installer to kick of the above commands and possible start the application following successful installation.

This installation mechanism has been packaged in an installed for CDs. Please contact Formaria for futher details.

Running a Aria application from a web page

Using the bundle.jar file from the first section in this chapter, a web page can be created which will start it as an applet. This assumes that the project was written using the AWT packages and not the swing packages.

Applet tag for the applet

  1. <APPLET style="height=100" archive="../lib/bundle.jar"
  2. width=100 code=org.formaria.awt.Applet>
  3. <PARAM NAME="startfile" VALUE="startup.properties">
  4. </APPLET>

It is possible to start an application written with the Swing packages as an applet by using the object tag

Starting a swing application from a browser

  1. <Object
  2. classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
  3. codebase="http://java.sun.com/products/plugin/1.1/jinstall-11-win32.cab#Version=1,3,0,0"
  4. width=50 height=50>
  5.  
  6. <param name="code" value="org.formaria.swing.Applet.class">
  7. <param name="codebase" value="./lib/bundle.jar/">
  8. <param name="startfile" value="startup.properties">
  9. </object>

Creating a setup for your Aria application

There are a number of ways of deploying your Aria application from custom written deployments to third pary installers. In this section, however, the installer which will be used is izPack, an open source installer which can be downloaded from http://www.izforge.com/izpack/

The version of izPack being used in this section is 3.6.1. For other versions or more advanced information please visit the izforge website.

Setup izPack by downloading the IzPack-install-3.6.1.jar file and double-clicking it to install. For these examples it is assumed that izPack is installed into c:\programmer\IzPack

For convenience it is easier to create an izPack directory under the Aria project root directory. Within this directory the following izPack configuration xml file can be created.

The izPack configuration file

  1. <?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
  2. <installation version="1.0">
  3. <info>
  4. <appname>Aria Installation</appname>
  5. <appversion>1.0</appversion>
  6. <authors>
  7. <author name="Joe Bloggs" email="joe.bloggs@mydomain.org"/>
  8. </authors>
  9. <url>http://www.mydomain.net/</url>
  10. </info>
  11.  
  12. <guiprefs width="640" height="480" resizable="no"/>
  13.  
  14. <locale>
  15. <langpack iso3="eng"/>
  16. </locale>
  17.  
  18. <resources>
  19. <res id="LicencePanel.licence" src="Licence.txt"/>
  20. <res id="InfoPanel.info" src="Readme.txt"/>
  21. <res id="shortcutSpec.xml" src="shortcutSpec.xml"/>
  22. </resources>
  23.  
  24. <native type="izpack" name="ShellLink.dll"/>
  25.  
  26. <panels>
  27. <panel classname="HelloPanel"/>
  28. <panel classname="InfoPanel"/>
  29. <panel classname="LicencePanel"/>
  30. <panel classname="TargetPanel"/>
  31. <panel classname="PacksPanel"/>
  32. <panel classname="InstallPanel"/>
  33. <panel classname="ShortcutPanel"/>
  34. <panel classname="FinishPanel"/>
  35. </panels>
  36.  
  37. <packs>
  38. <pack name="Base" required="yes">
  39. <description>The base files</description>
  40. <file src="Licence.txt" targetdir="$INSTALL_PATH"/>
  41. <file src="../bundle.jar" targetdir="$INSTALL_PATH"/>
  42. </pack>
  43. </packs>
  44. </installation>
  45.  

The info section of the file contains some basic information about the application being deployed. The guiprefs section will specify the size of the installation application. The locale can be modified to handle multiple languages.

The panels section specifies the screens which will make up the installation application. If there is a licence file for the application it can be specified here otherwise the LicencePanel node can be removed. Similarly, the InfoPanel displays a readmed for the application you are installing and can be removed if not appropriate.

Now all that is left to do is to create the installation jar file. This is done by creating a batch file in the izPack directory as follows:

Create the installation jar file

  1. "c:\Programmer\izpack\bin\compile" install.xml -b . -o install.jar -k standard

When run, the batch file will create the install.jar file. When double-clicked this file will start the installation script.

This jar file will only run if the Java runtime is installed on the machine. It is possible to automate the installation of the Java runtime by downloading the izpack-launcher-1.1.zip from the izforge website. All that is required then is to copy the dist/launcher-Win32.exe and the src/launcher.ini files to the izPack directory. The launcher.ini file is then modified to reflect the name of the installation jar.

The modified Launcher.ini file

  1. # Global entries, can be overriden by specific ones.
  2. jar = install.jar
  3. download = http://www.java.com/
  4.  
  5. # Win32 specific entries
  6. [win32]
  7. jre = jre/setup.exe

The download key specifies the source from which the Java runtime can be obtained in the event that it cannot be found on the system

The jre key specifies the subdirectory which contains the Java runtime which has been distributed with the setup. This setup for the Java runtime would typically be included when a CD installation is being distributed.

Compilation, Build and Test

Compile, build and test

Java applications must be compiled prior to executing and Aria applications are no exception to this rule. Aria uses Java code for response methods of all sorts so almost all Aria applications will require some compilation. Fortunately this is rarely a complicated process.

Testing is an important consideration for any application, compiled or not and Aria includes easy to use tools to assist in testing.

Debugging

Aria is delivered in two builds, one with debugging information and tracing switched on and the other with debugging switched off.

In the debug version, the logging level can be configured via the LogLevel parameter. The console trace provides much useful information about the loading of resources and access to the data model.

The log level can be set to several levels

  1. Logging Levels

SILENT = -1

No logging is used

NORMAL = 0

Little or no logging is used. Errors are echoed to the console

DEBUG = 1

Warnings and errors are logged to the console

VERBOSE = 2

Extensive error, warning and activity logging

PARANOID = 3

Everything possible is logged

in silent mode almost nothing is logged to the console whereas in paranoid mode everything possible is logged.

When building an application it is normally best to run with a debug version of Aria as it will give most information about possible problems and errors. When an application is run in debug mode additional checks are made along with additional diagnostic messages. When an application is shutdown the Aria framework will then output a summary of the logging information to the console. You should check this information for the presence of any errors or warnings. If there are errors you can then review the console output for more detailed information.

The logging some messages can also be suppressed by adding a DebugZones.properties file to the project. The file specifies the named zones where logging is to be enabled or disabled, in which case the messages and warnings associated with the zone are not output. Turning off some logging can be useful when alot of data is output, as is the case when a VERBOSE logging mode is used.

Sample DebugZones.properties file

  1. # Turn debug zones on/off at design time
  2. TRANSLATION=off
  3. RESOURCES=off

Beyond logging

Sometimes logging is not enough to debug an application and in such cases an interactive debugger can be of great benefit. Several commercial and open source development environments are available including JBuilder, NetBeans and Eclipse. In addition we provide an integrated debugger plugin based on Netbeans.

When using any of these debuggers we recommend downloading the Aria source code.

Debugging with NetBeans

NetBeans is a powerful development platform and features a rich debugging facility. Invoking the NetBeans debugger is simply a matter of right clicking on the Aria project of interest in the Projects window and choosing the Debug option. If your project is the default project you can just click on the debug button on the toolbar or choose the Debug Main Project option from the Run menu

You may also want to add the source code for the Aria platform to the project to allow you step into the Aria source code. Sometimes this can be useful if you are having problems with event handling code or in situations where the information output to the console is not enough for you to discern the immediate cause of a problem.

Once NetBeans starts debugging the application you get a lot of useful information about the framework. Note in the following example when NetBeans stops at a breakpoint how the window in the bottom right contains much information about inherited (from Page) variables:

The NetBeans environment includes on-line help regarding debugging and using the built-in debugger, please refer to this documentation for further information and instruction on debugging your applications.

Using JBuilder

Aria libraries can be used with commercial IDEs such as JBuilder. Configuring JBuilder for use with Aria is a simple process.

Under the project properties add a new library entry for Aria and point it at the Aria class files and resources.

Then add this library to the list of required libraries in the project. Once the new library has been added to the project choose edit and set the source file path.

Finally setup the runtime configuration in the Project Properties Run tab.

Then you are ready to debug the project using JBuilder. From the Run menu you can set the configuration or you can choose the specific configuration when starting the debug session via the toolbar.

Automating testing

TestPilot is a module included in Aria to allow recording of applet/application sessions. These sessions can then be played back to reproduce the test. The tests can be scripted so that input values can be modified and configured as repeated tests.

Aria applications can also be driven from an OpenOffice or Excel spreadsheet where input variables are loaded from a set of workbook sheets and the output saved (along with the inputs) to another workbook.

Troubleshooting

So you've compiled, built and tested and the application doesn't work. What do you do next? This chapter covers some of the things to check and some of the techniques that can be used to diagnose problems.

I've built my application but it doesn't run

Run a debug version !

The debug build of the Aria libraries output much extra debug and diagnostic information. If you are having trouble with your application the first thing you should consider is using the debug libraries.

The debug log will also summarizes the number or errors and warnings encountered when running the application. The summary is output to the console when the application exits (this requires a recent JDK).

The NetBeans log file may also provide additional debug information (they are stored under your user profile), but this should normally be a last resort.

Check the classpath

The classpath is often a source of problems for Java applications. Either a file or resource is missing from the classpath or some other object is loaded instead of the intended resource. Typically such problems manifest themselves as ClassNotFoundException or FileNotFoundException .

If you are using third party resources it is also worth checking the documentation for dependancies as one Jar may depend upon another. Worse still there are sometimes version dependancies (for example on the Servlet jar).

As a fallback and as a way of reducing dependancies you may be able to extract the classes you need from a Jar and use only those classes that you absolutely require. However this is a fairly drastic measure and should be used as a last resort.

Check the case

Remember Java is case-sensitive and even if Windows isn't, your paths may still need to be in the right case. If you are loading resources from Jars then the file names and paths must be in the correct case.

Check the log

Quite often the problem that stops your application running correctly is buried a few layers down. The debug log will immediately show many errors but it may appear that some are unrelated.

An example of this occurs when you try to invoke an event handler. The event is invoked dynamically and if there is a problem finding or invoking the method then a fairly generic error is generated saying that there was a failure invoking the method. While this effectively masks the real cause of the error it should give you enough information to know where to begin debugging.

Debug

If you have the type of problem described above it is usually a good idea to step through your event handlers. You may also want to put a breakpoint on your class's constructor to ensure that it is setup properly.

Since Aria is an open source framework you can also include or reference the source code in your project. With the debug libraries you can then set breakpoints within the library or at the point of any exception. Tracing back along the stack should help you locate and diagnose the problem.

Isolate the problem

If all else fails follow the divide and conquer rule and split your application into smaller parts. Keep splitting until you can isolate the problem and reliably reproduce the problem. Once you know the exact location of the problem finding the exact cause should only be a small step.

Only part of my page is appearing

Check the paths

Sometimes a page can embed other pages (via the include element), files or graphics, these resources must be located by the framework in order to display the application properly. In debug mode the framework outputs the names of the files it attempts to load. Several attempts may be made to load a file from various points on the classpath so you may get multiple warnings about a file not being loaded. In any case if the page is not displaying properly or if images are missing then a good point to start is by checking the log for warnings and errors messages.

Normally a Aria application points to the application's root folder and the pages, resources, and lang folders. The classpath may also point to other folders or Jars. A file should be addressable via the classpath so you may also need to include the part of the path that is not included in the classpath if your resource is located in a child folder (e.g. the path/file reference should be images/myimage.gif for a file such as x:\myapp\resources\images\myimage.gif when the framework runs the application from the x:\myapp\ folder ).

If you have a more elaborate classpath it is also worth checking that you don't have other versions of the file higher up on the classpath. Again checking the debug log will tell you exactly which file was loaded.

Check the build

Sometimes with multiple layers of an application it is possible that parts of your application are not up-to-date. You can do a clean build to resolve some of these issues but you may still have parts of your application that are not compiled or built. With dynamic loading and invocation via reflection a missing component may not be obvious as the cause of a problem. A quick check of the filesystem should reveal anything missing from the build following a clean.

Check the XML is properly formed

Aria relies heavily on XML and while this is very useful it can provide a source for numerous problems. Aria itself and Aria allow the XML parser to be replaced and the two can use different parsers so errors can emerge in certain rare situations.

Perhaps the biggest cause of problems is hand-coding of XML. It is very easy to make hard to locate errors in XML when hand-coding. Even something so simple as a missing space or a missing quotation mark can lead to parsing errors. Ultimately these parsing errors can lead to failures or partial failures such as pages only appearing in part.

To rectify these problems first off avoid using editors like notepad as they don't provide any explicit support for XML. There are several excellent XML editors available and many are available as free open source applications. As a minimum you should be able to check that the XML is well formed.

Next you should follow the divide and conquer approach and comment out sections of the offending XML file till you locate the section that causes the fault. Java is case sensitive and therefore it is important that elements, attributes and values are spelled correctly and are in the right case.

Finally, even if you don't want to use Aria's built-in editors you should be able to use Aria to build a sample or fragment of what you are trying so as to check the syntax. once you have the right syntax and structure you can modify the fragment to your needs and simply paste it into your own file.

The 3d effect on buttons is missing

Check the Look and Feel.

Depending upon the look and feel you have chosen for the application buttons and other controls will have different appearances. Some look and feels allow more customization than others. In the default look and feel (and on later JDKs) setting the style on a component such as a button will override the default settings. The run-time appearance may then be significantly different from that shown within the editor since the editor does not modify the look and feel.

If you want something closer to the look and feel used within the editor you can choose the Windows look and feel within the editor (for Windows systems; on other systems you will have to set the LAF explicitly). The Windows LAF preserves the 3D appearance of components such as buttons. For more information see See Setting the system look and feel.

I get an exception when using Synth

The expressions in the synth file are incorrect

The .xynth file is quite sensitive to errors. If you have an incorrectly formed expression it can cause numerous errors in parsing the file or processing the content. The error message produced in this sitution isn't very informative, for example:

  1. Uncaught error fetching image:
  2. java.lang.NullPointerException
  3. at sun.awt.image.URLImageSource.getConnection(URLImageSource.java:97)
  4. at sun.awt.image.URLImageSource.getDecoder(URLImageSource.java:106)
  5. at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:240)
  6. at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
  7. at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)

In practice this usually means that the image name was incorrect or invalid. If the image is a preprocessed file check that the expression is properly formed. Check that there is the leading $ symbol followed by the expression within opening ` { ` and closing ` } ' currly braces.

Section IV: Reference

Components

Aria UI Components

Aria adds many user interface (UI) components to the Aria framework, while some of these components are non-visual components, the extensions offered by Aria can greatly improve the overall visual impact of your application. This section documents some of the Aria specific components. The components fall into a number of categories although some components are related.

Graphical Components

Shape

Class : org.formaria.swing.Shape

The shape component is a simple graphical object that renders some simple shapes. The component itself is of limited benefit on its own but it can be used with some of Aria's flowed text components to nest other objects that would otherwise have a rectangular area.

In the example below an image component is embedded in an ellipse shape, and the shape then interacts with a FlowedText component to alter the text layout. The shape may also be defined programmatically.

The XML for the above example is shown below:

Nesting an image within a shape

  1. <Components>
  2. <Panel name="textPanel" x="10" y="10" w="800" h="600" border="1" style="palePanel">
  3. <FlowedText antialias="false" name="flowedTextArea" cols="1"
  4. x="200" y="10" w="680" h="580" style="base/edit"
  5. content="<b>Lorem ipsum..." opaque="false"/>
  6. <Shape name="myShape" x="10" y="10" w="400" h="400" mode="13" style="blue">
  7. <Image name="imageA" x="0" y="0" w="400" h="400" content="glyph.jpg"/>
  8. </Shape>
  9. </Panel>
  10. <Button name="backBtn" content="Back" x="10" y="670" w="100" h="20"/>
  11. <Button name="modeBtn" x="710" y="670" w="100" h="20" content="Mode" />
  12. </Components>

The component is also used to draw some simple adornments like lines using the component foreground and background colors.

Shape drawing modes

Mode

Description

RECTANGLE = 0

A plain rectangle

ORTHO_LINE = 1

An orthogonal line

EXTRABOLD_HORIZONTAL = 2

Draws an extra bold line horizontally across the top of the component (3 pixels deep)

BOLD_HORIZONTAL = 3

Draws an bold line horizontally across the top of the component

NORMAL_HORIZONTAL = 4

Draws an line horizontally across the top of the component (1 pixel deep)

THIN_HORIZONTAL = 5

Draws an thin line horizontally across the top of the component

EXTRABOLD_VERTICAL = 6

Draws an extra bold line vertically across the top of the component (3 pixels deep)

BOLD_VERTICAL = 7

Draws an bold line vertically across the top of the component (2 pixels deep)

NORMAL_VERTICAL = 8

Draws an line vertically across the top of the component (1 pixel deep)

THIN_VERTICAL = 9

Draws an thin line vertically across the top of the component ( < 1 pixel deep)

RIGHT_TOP_LINE = 10

Draws a line from the top left to the bottom right

LEFT_TOP_LINE = 11

Draws a line from the top right to the bottom left.

ELLIPSE = 12

Draws an ellipse

SOLID_ELLIPSE = 13

Draws and fills an ellipse

SOLID_DIAMOND = 14

Draws a diamond

DIAMOND = 15

Fills a diamond

USER = 16

Draws a user defined shape

USER_FILL = 17

Fills a user defined shape

USER_FILL_DRAW = 18

Fills and draws a user defined shape

EasterEgg

Class : org.formaria.swing.EasterEgg

The easter egg component is an unusual component in that most of its function is designed to be hidden from the average user. The component is designed to allow restricted access to key features of an application, typically for a project administrator.

Under the hood the EasterEgg component is an extension of the Image component, so normally it will display an image and uses the image controls normal attributes.

The component relies on the org.formaria.event.ClickListener class to monitor the user interaction. Using this interface you can define a special key sequence of set of mouse clicks, or a click on a particular area to enable access to a restricted part of the application. By default the component responds to a mouse click anywhere within the component's client area by popping up a password dialog.

Ultimately when the required interaction has occurred a password dialog can be displayed. On successful login the user can then navigate to a restricted page or whatever the author decides via a callback method specified with the setCallback api.

ReflectedImage

The reflected image component displays an image and a reflection of that image. For example:

In the above image the top half of the image is the unadorned image and the bottom half is the dynamically generated reflection.

Attribute

Description

shear

The factor by which to shear the image reflection (0-100)

content

The image name

tooltip

The tooltip text for this component

opaque

true if the component is to fully paint the client area

CaptionedImage

The captioned image component displays an image and a caption below that image. Both the component itself and the image it contains may draw a border.

Attribute

Description

border

0 for no border, 1 for an outer border and 2 for an inner and outer border

captionStyle

The style name for the caption text

imageStyle

The stylename for the image and its border

style

The style name for the components main area

fill

true to fill the background

pad

The amount of padding around the image in pixels

arc

The amount of rounding on the borders

caption

The caption text

minHeight

The minimum component height

minWidth

The minimum component width

opaque

true if the component is to fully paint the client area

buffered

true if the component is to buffer its painting operations

tooltip

The tooltip text

align

The caption text alignment

alignment

The caption text alignment

shadow

true to draw a shadow on the image

imageName

The filename of the image

content

The filename of the image

enabled

true if the component is enabled/selectable

blend

true if the backgroudn painter is to blend into its parent

translucent

true if the component is translucent

laf

true to use the look and feel settings

painter

the class name of the background painter

titlePos

The position of the title text on the main component

titleStyle

The style for the title text

title

The text for the title

RollupBar

Class: org.formaria.swing .RollupBar

An outlook style roll-up bar. The methods are modelled on the JTabbedPane.

Attribute

Description

alignment

Set the component alignment (Left | Right | Center | Leading | Trailing)

icon

The filename of the icon

iconpressed

The filename of the pressed icon

iconrollover

The filename of the rollover icon

imagename

The filename of the image

pressed

The filename of the pressed image

rollover

The filename of the rollover image

disabled

The filename of the disabled image

style

The style for the component and its children

buttonheight

The height of the buttons contained within the component

painter

The class name of a painter for the buttons, for example org.formaria.swing.painter.GradientButtonPainter or
org.formaria.swing.painter.GradientToggleButtonPainter

antialias

Turn antialias on or off, true to turn it on or false to turn it off

ProgressIndicator

Class: org.formaria.swing.ProgressIndicator

Provides an animated circular area consisting of bars which gives an indicator of progress.

HyperLabel

Class: org.formaria.swing.HyperLabel

The hyper label allows hyper linking within an application, showing an underline when unde the cursor and enabling button like actions on mouse click events.

Attribute

Description

antialias

Turn antialiasing on or off, true to turn anitaliasing off or false to turn it off.

alignment

The text alignment

tooltip

The tooltip text for this component

Text Components

As you may have already noticed the components provided by Aria generally work in concert with one another. The text components interact with one another and with components like the Shape component described above. This section provides some details of the text rendering components.

If you don't find the text manipulation that you need remember that you can always create your own component using Java and Java2D. Employing such a component within a Aria application is pretty easy. The text components presented here form a hierarchy and all use such a mechanism and are easily extended and customized.

FlowedTextComponent

Class : org.formaria.swing.FlowedTextComponent

The flowed text component is a text component that flows text around components that overlap the area belonging to this component. The components may be any sibling of the component including images, shapes and other user interface components. Most components are rectangular but some of Aria's components like the shape component can define other areas.

The flowed text components are designed to allow desktop publishing like presentations and layouts. To achieve overlapping components you need to either choose the null layout or some other layout that can support overlapping components. Aria's GuideLayout can do just this.

The layout for the above page is defined as follows:

XML for a flowed text component

  1. <Page class="aria.samples.charts.Flows" >
  2. <Components>
  3. <Panel name="overlayPanel" x="10" y="10" w="800" h="600" style="blue" opaque="false"/>
  4. <Panel name="textPanel" x="10" y="10" w="800" h="600" style="blue">
  5. <Label content="Aria Times" x="10" y="0" w="300" h="50" style="blue/title"/>
  6. <Label content="Your source for Aria news." x="10" y="45" w="300" h="30"
  7. style="blue/subtitle" opaque="false"/>
  8. <Label content="City Edition" x="430" y="15" w="80" h="55" style="blue/sidebar"
  9. opaque="true" align="center"/>
  10. <Shape mode="2" x="10" y="80" w="500" h="5" style="blue/title" />
  11. <Shape mode="5" x="10" y="85" w="500" h="5" style="blue/title" /></P>
  12. </TD>
  13. </TR>
  14. <TR>
  15. <TD ROWSPAN="1" COLSPAN="1">
  16. <P>
  17. <Image name="imageA" x="10" y="260" w="160" h="120" content="trophy.jpg"/>
  18. <Image name="imageB" x="350" y="150" w="160" h="120" content="measure.jpg"/>
  19. <FlowedTextComponent name="flowedTextArea" x="10" y="100" w="500" h="480" style="blue"
  20. content="Lorem ipsum dolor ...." opaque="false"/>
  21. &nbsp;</P>
  22. </TD>
  23. </TR>
  24. <TR>
  25. <TD ROWSPAN="1" COLSPAN="1">
  26. <P>
  27. </Panel>
  28. <Button name="backBtn" content="Back" x="10" y="670" w="100" h="20"/>
  29. <Button name="overlayBtn" x="380" y="670" w="100" h="20" content="Overlay" />
  30. <Button name="clipBtn" x="490" y="670" w="100" h="20" content="Clip" />
  31. <Button name="showBtn" x="600" y="670" w="100" h="20" content="Show"/>
  32. <Button name="hideBtn" x="710" y="670" w="100" h="20" content="Hide"/>
  33. </Components></P>
  34. </TD>
  35. </TR>
  36. <TR>
  37. <TD ROWSPAN="1" COLSPAN="1">
  38. <P>
  39. <Events>
  40. <Event method="showPrevious" target="backBtn" type="ActionHandler"/>
  41. <Event method="showImages" target="showBtn" type="ActionHandler"/>
  42. <Event method="hideImages" target="hideBtn" type="ActionHandler"/>
  43. <Event method="clipImages" target="clipBtn" type="ActionHandler"/>
  44. <Event method="overlayImages" target="overlayBtn" type="ActionHandler"/>
  45. </Events>
  46. </Page>

The properties for the flowed text component are:

Attribute

Description

antialias

Turn antialiasing on or off, true to turn anitaliasing off or false to turn it off.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

cols

The number of columns

colSpacing

The column spacing

HtmlText

Class : org.formaria.swing.HtmlText

For compatibility reasons some of the text components in Aria do not handle html markup, even in the case of the Swing widgets. This component bypasses that rendering and provides an editor pane with Swing's HTMLEditorKit. The component is not a full HTML renderer like a web browser but it can accommodate some hyper linking within the text.

PolygonalTextArea

Class : org.formaria.swing.PolygonalTextArea

Using this component you can flow text within an arbitrary polygon. The component, like the FlowedTextArea clips any siblings from its area. The component is ideal for flowing text around things like images.

Similar to the example above for the FlowedTextComponent the layout for the PolygonalTextArea can be defined as follows:

 

  1. <Page class="aria.samples.charts.Flows" >
  2. <Components>
  3. <Panel name="overlayPanel" x="10" y="10" w="800" h="600" style="blue" opaque="false"/>
  4. <Panel name="textPanel" x="10" y="10" w="800" h="600" style="blue">
  5. <Label content="Aria Times" x="10" y="0" w="300" h="50" style="blue/title"/>
  6. <Label content="Your source for Aria news." x="10" y="45" w="300" h="30"
  7. style="blue/subtitle" opaque="false"/>
  8. <Label content="City Edition" x="430" y="15" w="80" h="55" style="blue/sidebar"
  9. opaque="true" align="center"/>
  10. <Shape mode="2" x="10" y="80" w="500" h="5" style="blue/title" />
  11. <Shape mode="5" x="10" y="85" w="500" h="5" style="blue/title" /></P>
  12. </TD>
  13. </TR>
  14. <TR>
  15. <TD ROWSPAN="1" COLSPAN="1">
  16. <P>
  17. <Image name="imageA" x="10" y="260" w="160" h="120" content="trophy.jpg"/>
  18. <Image name="imageB" x="350" y="150" w="160" h="120" content="measure.jpg"/>
  19. <PolygonalTextArea name="flowedTextArea" x="10" y="100" w="500" h="480" style="blue"
  20. points="0,0,500,0,500,40,350,40,350,90,460,185,500,185,500,480,0,480,
  21. 0,280,80,280,160,220,70,145,0,145,0,0"
  22. content="Lorem ipsum dolor sit amet, consecte..." opaque="false"/></P>
  23. </TD>
  24. </TR>
  25. <TR>
  26. <TD ROWSPAN="1" COLSPAN="1">
  27. <P>
  28. </Panel>
  29. <Button name="backBtn" content="Back" x="10" y="670" w="100" h="20"/>
  30. <Button name="overlayBtn" x="380" y="670" w="100" h="20" content="Overlay" />
  31. <Button name="clipBtn" x="490" y="670" w="100" h="20" content="Clip" />
  32. <Button name="showBtn" x="600" y="670" w="100" h="20" content="Show"/>
  33. <Button name="hideBtn" x="710" y="670" w="100" h="20" content="Hide"/>
  34. </Components></P>
  35. </TD>
  36. </TR>
  37. <TR>
  38. <TD ROWSPAN="1" COLSPAN="1">
  39. <P>
  40. <Events>
  41. <Event method="showPrevious" target="backBtn" type="ActionHandler"/>
  42. <Event method="showImages" target="showBtn" type="ActionHandler"/>
  43. <Event method="hideImages" target="hideBtn" type="ActionHandler"/>
  44. <Event method="clipImages" target="clipBtn" type="ActionHandler"/>
  45. <Event method="overlayImages" target="overlayBtn" type="ActionHandler"/>
  46. </Events>
  47. </Page>

The properties for the flowed text component are:

Attribute

Description

antialias

Turn antialiasing on or off, true to turn anitaliasing off or false to turn it off.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

clip

true to clip the sibling components from the flow area.

points

A series of X-Y value pairs defining the flow area's polygon.

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

cols

The number of columns, if a value of less than 1 is specified then the value is ignored.

colSpacing

The column spacing in pixels

ReflectedText

Class : org.formaria.swing.ReflectedText

Draws a single line string with a reflection of that string, this component is of limited use but may be useful for things like captions and titles.

The attributes of the component are detailed below:

Attribute

Description

antialias

Turn anti-aliasing on or off, true to turn anit-aliasing off or false to turn it off.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

shear

The X-axis shear factor / 100

RotatedText

Class : org.formaria.swing.RotatedText

Text can be rotated to any angle using this component.as illustrated below

The properties of the component are much like the other text components with the following properties

Attribute

Description

antialias

Turn anti-aliasing on or off, true to turn anti-aliasing off or false to turn it off.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

angle

The angle of rotation in degrees

cols

The number of columns, if a value of less than 1 is specified then the value is ignored.

colSpacing

The column spacing in pixels

Animations

AnimatedText

Class : org.formaria.swing.AnimatedText

This component fades text into view by cycling the opacity of the text from transparent to opaque and from an unsaturated version of the foreground color to the fully saturated foreground color.

Attribute

Description

antialias

Turn anti-aliasing on or off, true to turn anti-aliasing off or false to turn it off.

align

The horizontal text alignment. Values can be left , right or center

autostart

Automatically starts the animation when the component is displayed if a value of true is set, otherwise the animation does not commence till started by a call to the start method.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

sleep

The sleep time between animation steps in milliseconds, the default is 100 milliseconds

step

The step size in percent, the default is a 1% step

CreditsText

Class : org.formaria.swing.CreditsText

Just like the credits at the end of a movie this component scrolls the text vertically from top to bottom. At the top and bottom ends of the component the text is faded in and out.

The attributes for the credits component are described below

Attribute

Description

antialias

Turn anti-aliasing on or off, true to turn anti-aliasing off or false to turn it off.

align

The horizontal text alignment. Values can be left , right or center

autostart

Automatically starts the animation when the component is displayed if a value of true is set, otherwise the animation does not commence till started by a call to the start method.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

sleep

The sleep time between animation steps in milliseconds, the default is 100 milliseconds

step

The step size in percent, the default is a 1% step

fade

The percentage of the component's width over which the text is faded in and out. The default is zero, for which no fading occurs.

MarqueeText

Class : org.formaria.swing.MarqueeText

As an alternative to the credits text this component scrolls the text horizontally across its area, much like a ticker type component..

Attribute

Description

antialias

Turn anti-aliasing on or off, true to turn anti-aliasing off or false to turn it off.

align

The horizontal text alignment. Values can be left , right or center

autostart

Automatically starts the animation when the component is displayed if a value of true is set, otherwise the animation does not commence till started by a call to the start method.

content

The text to flow within the component. Within the text some HTML like tags can be specified, such as for bold, for italic, for strike through, and
for a break

text

A synonym for content

imagetype

A flag indicating how the animation is drawn. 0=create a buffered image for rendering, 1=don't use a buffer, >1 the value is used to get the BufferedImage type constant i.e. a value of 2 creates a BufferedImage with type BufferedImage.TYPE_INT_ARGB.

sleep

The sleep time between animation steps in milliseconds, the default is 100 milliseconds

step

The step size in percent, the default is a 1% step

fade

The percentage of the component's width over which the text is faded in and out. The default is zero, for which no fading occurs.

Input Controls

BaseTable

Class : org.formaria.swing.BaseTable

An extension of the basic table wrappers in the Aria framework. This table component adds some additional features, including...

The component is due to be consolidated into the Aria Table component in due course.

CheckList

Class : org.formaria.swing.CheckList

This component presents a list with check boxes for each list item, The component functions pretty much as a plain list except that the list items are rendered with checkboxes so that the user can make multiple selections. The component is useful when there are a lot of items to be selected.

DateEdit

Class : org.formaria.swing.DateEdit

The component provides facilities for editing dates, complete with an integrated visual date picker. The date picker shows the currently entered date and upon return updates the date field.

The current date is highlighted (dark blue/green) and moving the cursor highlights the selection (orange). In the title bar, four buttons allow changes of year and month in the forward and backwards direction.

Attribute

Description

format

The date format, an argument for the java.text.SimpleDateFormat class

style

The basic style for the chooser/edit field

selectedstyle

The style for the selected day

weekendstyle

The style name for the weekend days

highlightstyle

The style name for the highlighted day

headerstyle

The style name for the header

threedstyle

The style name for the 3D elements - buttons etc...

Keypad

Class : org.formaria.swing.Keypad

A simple numeric keypad designed for user in point-of-sale or kiosk situations where there may be no keyboard available for input. The keypad can be operated with the mouse.

The attributes are:

Attribute

Description

style

The basic style

selected3d

The style for the 3d elements

styleShade

The style for the shaded border

MoneyEdit

Class : org.formaria.swing.MoneyEdit

An edit field for monetary values. The user can specify an ISO 4217 currency code and a currency format along with a locale country and language.

Attribute

Description

format

The currency format

currency

The ISO 4217 currency code

language

The country code for the currency e.g 'EN', 'DA', 'FR'

country

The locale country code e.g 'IE', 'US', 'FR'

usePrefix

Turn the currency prefix flag on or off, true to turn it on or false to turn it off

useSuffix

Turn the currency suffix flag on or off, true to turn it on or false to turn it off

Spinner

Class : org.formaria.swing.Spinner

A spinner or up/down control. The component does not much in terms of functionality above what is available with the equivalent Swing component (in fact it's a very thin wrapper), however it does provide some useful integration with databings and so on.

The attributes are:

Attribute

Description

min

The minimum values

max

The maximum value

step

The step or value increment used to change the value

Slider

Class : org.formaria.swing.Slider

A slider for choosing a value within a continuous range of value, the slider can be arranged vertically or horizontally . The component does not much in terms of functionality above what is available with the equivalent Swing component (in fact it's a very thin wrapper), however it does provide some useful integration with databings and so on.

The attributes are:

Attribute

Description

min

The minimum values

max

The maximum value

step

The step or value increment used to change the value

Progress

Class : org.formaria.swing.Progress

An visual indicator of progress. The progress bar can also include an icon.(not shown below)

The attributes are:

Attribute

Description

progress

The current progress level in percent.

cue

The cue image filename. The cue is an icon displayed to the left of the progress bar.

bars

The number of bars to display.

step

The step or value increment used to change the value

max

The maximum progress value

PasswordDlg

Class: org.formaria.swing.PasswordDlg

A dialog box which can be used to handle password inputs.

Data Components

Graph

Class : org.formaria.swing.Graph

A graphing/charting component. Various chart types can be displayed including line, bar and pie charts. The component features rollover and tooltips to help users identify an area of the graph. Using these feedback mechanisms a user may also select an area of the chart as an input value. (The terms chart and graph are used interchangeably here rather than in the strict computer science terms).

The first step involved in setting up a chart is to define or locate the data for the chart. There are two options for setting the data, first you can pass the chart component a series of x-y data points or values (one complete series followed by the next series if any), or you can reference a model node. Using a model node is probably the most convenient mechanism and allows you to use database tables and queries as a source of data for the chart.

In the following examples we will use the data from the SugarCRM sample database. The data can be retrieved using the following query (note that in a real deployment situation you may not actually access the data directly as shown here). The SQL for the example is shown below in the table data sources XML file.

A query to select some sample data

  1. <ResultSet id="Outcome" conn="SugarConn"
  2. sql="SELECT lead_source,sales_stage,sum(amount_usdollar/1000) as total,
  3. count(*) as opp_count
  4. FROM opportunities
  5. WHERE opportunities.lead_source IN ('','Cold Call','Existing Customer',
  6. 'Self Generated','Employee','Partner','Public Relations',
  7. 'Direct Mail','Conference','Trade Show',
  8. 'Web Site','Word of mouth','Email',
  9. 'Other')
  10. AND opportunities.deleted=0
  11. GROUP BY sales_stage,lead_source
  12. ORDER BY lead_source,sales_stage"/>

Once you have setup a data source you can very quickly setup a chart, for example the following shows a simple page contain just a chart:

Adding a graph to a page

  1. <Page class="aria.samples.charts.LineChart">
  2. <Components>
  3. <Graph name="lineChart" x="10" y="10"
  4. w="800" h="600" mode="1" labels="false"
  5. markers="true" border="true" legendPos="4"/>
  6. </Components>
  7. </Page>

Once the chart has been added to the page it is necessary to connect the data (later we will see how to do this with a data binding). Within the Java code we may want to change the data displayed by the chart or even the chart type used for display purposes so we create a setup method. Normally this setup method would be called from the pageCreated method to initialize the chart.

Setting up the chart data

  1. protected void setupChart( boolean animate, boolean outputTable )
  2. {
  3. DataModel root = ProjectManager.getModel();
  4. DataModel chartData = (DataModel)root.get( "Outcome" );
  5. if ( chartData != null ) {
  6. chart.setTitle( "Opportunities by Outcome" );
  7. chart.setXScale( -45, 20, 0, "Outcome", null );
  8. chart.setYScale( chart.LINEAR, "Value" );</P>
  9.  
  10. <P>
  11. chart.setModelStructure( chart.SERIES, 1 );
  12. chart.setModelStructure( chart.LABELS, 0 );
  13. chart.setModelStructure( chart.VALUES, 2 );
  14. chart.setModel( chartData );
  15. }
  16. </P>
  17. </TD>
  18. </TR>
  19. <TR>
  20. <TD ROWSPAN="1" COLSPAN="1">
  21. <P>
  22. else {
  23. // Alternatively setup some dummy data for DEMO purposes
  24. numSeries = 5;
  25. int numPoints = 14;
  26. int seriesPts = numPoints * 2 * numSeries;
  27. capacityPoints = new double[ seriesPts ];
  28. &nbsp;
  29. double te[] = { -45.0, -40.0, -35.0, -30.0, -25.0, -20.0,
  30. -15.0, -10.0, -5.0, 0.0, 5.0, 10.0,
  31. 15.0, 20.0 };
  32. double ts[] = { 0.9, 1.0, 1.1, 2.0, 2.1 };
  33. &nbsp;
  34. int idx = 0;
  35. for ( int j = 0; j < numSeries; j++ ) {
  36. for ( int i = 0; i < numPoints; i++ ) {
  37. capacityPoints[ idx++ ] = te[ i ];
  38. capacityPoints[ idx++ ] = te[ i ] * te[ i ] * ts[ j ];
  39. }
  40. &nbsp;
  41. }
  42. showChart( 1, seriesNames, animate );
  43. }
  44. }
  45. &nbsp;

In setting up the data in conjunction with a data model the important thing to do is to tell the chart where the data is found. The chart expects a tabular structure within the data even if the data does not originate in a database (you can define the data in static XML or in any other model so long as it conforms to the tabular structure that the chart expects). The setModelStructure method is used to tell the chart which attributes (field/columns) of each record contain the series names, the x-values or labels and the y-values. If you can also store the data series in a set of fields for each row or x-value, in which case you do not specify a column as identifying the series.

The data for the sample charts can be seen below:

By changing the mode of the chart either through the XML mode attribute or programmatically via the setMode method you can change how the data is displayed. Using exactly the same data the chart is shown in various modes..

First off, the scatter plot mode. Normally this chart type is used to show discrete data or values that are not closely linked to one another. For the sample data you can probably see that the scatter plot is not very informative. Moving the mouse over a point highlights the point and a tooltip is displayed indicating the values associated with that point.

Next a line chart is shown. This type of chart is similar to the scatter plot except that the points in the series are connected. Normally such a chart is used for data that exhibits some sort of trend or pattern.

Bar charts are frequently used in business applications and they can help illustrate trends and relationships between small data sets. Bar charts are normally used where the various data points represent discrete samples (whereas a line chart is more typically used to represent continuous samples). In the case of the sample data there are probably too many data points to make this chart really useful, but reducing the number of series displayed would make the chart a lot clearer.

A variation of this chart type is the stacked bar chart where the data from each series (at corresponding x-value) are stacked upon one another vertically. In a business case stacked bar charts are often used to illustrate cumulative results. In the case of the sample data this is probably the most useful chart type.r

Finally pie charts. The pie charts are slightly different to other chart types in that they only display a single series

If you need charting capabilities beyond these simple chart types you can extend the component or alternatively you can plug-in a more complete charting package such as the open source JFreeChart or any of the commercial charting libraries.

TreeTable

Class : org.formaria.swing.TreeTable

An advanced tree-table component. The first column of the table is represented by a tree so that recurring data or families of rows can be represented by a single row in the table.

The attributes are:

Attribute

Description

headingstyle

Set the heading style name

selectionstyle

Set the name of the style for the current selection

borderstyle

The border style

updatemodel

true to update the model with the current selection state

See the API documentation for more details on other methods for configuring the tree table.

Multimedia Components

Audio

Class : org.formaria.swing.Audio

This component allows you play an audio stream. Support for sound file formats varies depending upon the availability of Sun's Java Media Framework and the installation of codecs. However for the built-in file types no additional software is required.

Video

Class : org.formaria.swing.Video

This advanced component allows you play a video stream. Support for video file formats varies depending upon the availability of Sun's Java Media Framework and the installation of codecs.

The component can also support nested components that overlap the video playback area. Good visual effect can be obtained by using say a looping vido clip as the background to something like a logon screen where otherwise there may be a lot of empty space.

The above screen can be created very easily, here's the XML:

Creating a video backed logon screen.

  1. <Page layout="border">
  2. <Components>
  3. <Video name="myVideo" loop="true" start="true" content="myVideo.mpg" controller="false"
  4. constraint="center">
  5. <Panel x="150" y="150" w="330" h="160" style="synthPanelLight" blend="true"
  6. painter="org.formaria.swing.LogoBackground" border="2">
  7. <Panel x="1" y="1" w="328" h="40" style="synthPanelLight">
  8. <Image x="10" y="5" w="30" h="30" content="users.gif"/>
  9. <Label x="45" y="10" w="250" h="20" opaque="false"
  10. content="Please enter you logon credentials" style="title"/>
  11. </Panel>
  12. <Label x="30" y="60" w="80" h="20" align="right" opaque="false" content="Username"
  13. style="base/prompt"/>
  14. <Label x="30" y="80" w="80" h="20" align="right" opaque="false" content="Password"
  15. style="base/prompt"/></P>
  16. </TD>
  17. </TR>
  18. <TR>
  19. <TD ROWSPAN="1" COLSPAN="1">
  20. <P>
  21. <Edit x="120" y="60" w="190" h="19" align="right" opaque="false" content="sa"
  22. style="base/edit"/>
  23. <Password x="120" y="82" w="190" h="19" align="right" opaque="false" content=""
  24. style="base/edit"/>
  25. <ImageButton x="120" y="110" w="90" h="30" opaque="true"
  26. imageName="button.png" text="Cancel" rollover="button_over.png"
  27. pressed="button_pressed.png" style="base/imagebutton"/>
  28. <ImageButton x="220" y="110" w="90" h="30" opaque="true"
  29. text="Logon" imageName="button.png"
  30. rollover="button_over.png" pressed="button_pressed.png"
  31. style="base/imagebutton"/>
  32. </Panel>
  33. </Video>
  34. </Components>
  35. </Page>

Note how the video component is effectively used as a panel within which to host other controls. This is a slight trick and some caution is needed in using this approach as the first component nested must be a panel, failing to allow for this can lead to unpredicatble painting or even invisible children, but fortunately you should be able to simple insert the extra panel in most situations.

The start and loop attributes control the playback, initiating automatic playback on start-up and then repeated or loop playback once the end of the video clip is reached. A small control panel can also be displayed to control playback (ideally for longer clips) by setting the controller attribute to true .

The attributes for the video component are:

Attribute

Description

content

Set the name of the video file to play

loop

true to loop the video, restarting the video playback

start

true to autostart the video

controller

true to show the video controller/progress bar

Navigation Components

  • BreadcrumbBar
Class: org.formaria.swing.BreadcrumbBar

The Breadcrumb Bar can be used to present a navigation history by listing links to the pages used to navigate to the current page.

If the visible navigation history exceeds the size of the component the component will scroll the navigation either left or right via arrow keys automatically prefixed and appended to the component.

When a link is selected an ActionEvent is fired and the listener can respond to the event. The page name associated with the link is passed with the event as the action command. Once the link has been clicked the links to the right of the select link are removed and the bar is refreshed.

Section V: Case Studies

Case Study: Mortgage

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 Aria project on the sourceforge site. The intention of the tutorial is the make the developer familiar with development elements of the Aria/Aria 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

Introduction

This step shows how to set up the basic structure of a Aria 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 ValidationFactory. 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.

Aria Editor

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

Aria

In introducing Aria the application is makes use of the routes which are available in the Aria 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 Aria application as shown in the diagram below.

The run.bat file contains the text below.

The run.bat file

  1. java -cp .;lib;AriaRuntimeCommon.jar;images;pages;
  2. resources;build;classes org.formaria.swing.Applet

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

The frames.xml file

  1. <FrameSet>
  2. <Frame name="banner" constraint="NORTH" content="banner" width="640" height="96"/>
  3. <Frame name="content" constraint="CENTER" content="Welcome" width="640"
  4. height="288"/>
  5. <Frame name="navPanel" constraint="SOUTH" content="navPanel"
  6. width="640" height="96"/>
  7. </FrameSet>
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

navPanel.xml page

  1. <Page class="org.formaria.mortgage.NavPanel" style="Heading">
  2. <Components>
  3. <Image name="nextButton" x="564" y="51" w="19" h="18" content="right.gif"/>
  4. <Image name="prevButton" x="60" y="54" w="19" h="18" content="left.gif"/>
  5. </Components>
  6. <Events>
  7. <Event method="nextPage" target="nextButton" type="MouseHandler"/>
  8. <Event method="prevPage" target="prevButton" type="MouseHandler"/>
  9. </Events>
  10. </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.

The NavPanel class

  1. package org.formaria.mortgage;
  2.  
  3. import org.formaria.aria.*;
  4.  
  5. public class NavPanel extends Page
  6. {
  7.  
  8. public void nextPage()
  9. {
  10. if ( wasMouseClicked())
  11. navigateToPage( "next" );
  12. }
  13.  
  14. protected void navigateToPage( String key )
  15. {
  16. Page target = ( Page )( ( Target ) pageMgr.getTarget( "content" ) ).getComponent( 0 );
  17. String dest = ( String )target.getAttribute( key, null );
  18. if ( dest != null )
  19. pageMgr.showPage( dest, "content" );
  20. }
  21.  
  22. public void prevPage() {
  23. if ( wasMouseClicked() )
  24. pageMgr.showPrevious();
  25. }
  26. }

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.

The bindings on the personal page

  1. ...
  2. <Data>
  3. <Bind target="firstnameText" source="mortapp/${getCustomerID()}/firstname"
  4. output="mortapp/${getCustomerID()}/firstname" />
  5. <Bind target="surnameText" source="mortapp/${getCustomerID()}/surname"
  6. output="mortapp/${getCustomerID()}/surname" />
  7. <Bind target="dobText" source="${getCustomerID()}/dob"
  8. output="mortapp/${getCustomerID()}/dob" />
  9. <Bind target="titleList" source="defaults/titleList" output="mortapp/${getCustomerID()}/title" />
  10. </Data>
  11. ...

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

The callback function in the Personal page

  1. public String getCustomerID()
  2. {
  3. return "customer" + ( String )currentCust.get();
  4. }

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.

The Finish class

  1. public class Finish extends Page
  2. {
  3. public void pageActivated() {
  4. try {
  5. String path = System.getProperty( "user.dir" ) + File.separator + "save.xml";
  6. FileOutputStream fos = new FileOutputStream( path );
  7. OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" );
  8. BufferedWriter bw = new BufferedWriter( osw );
  9. DataSource.outputModel( bw, ( ( DataModel ) rootModel.get( "aria_state/mortapp" ) ) );
  10. bw.flush();
  11. bw.close();
  12. }
  13. catch (Exception e) {
  14. System.out.println( "error" );
  15. }
  16.  
  17. }
  18. }

This provided us with the following output.

Model data saved from the application

  1. <data id="mortapp">
  2. <data id="customer1">
  3. <data value="Joe" id="firstname" />
  4. <data value="Bloggs" id="surname" />
  5. <data value="14/12/1975" id="dob" />
  6. <data value="Mr" id="title" />
  7. </data>
  8.  
  9. <data id="customer2">
  10. <data value="Josephine" id="firstname" />
  11. <data value="Bloggs" id="surname" />
  12. <data value="01/04/1972" id="dob" />
  13. <data value="Mrs" id="title" />
  14. </data>
  15.  
  16. <data id="finance">
  17. <data value="250000" id="propvalue" />
  18. <data value="200000" id="mortamt" />
  19. </data>
  20. </data>
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.

The localized personal page

  1. <Page next="personal" class="org.formaria.mortgage.Welcome" resource="">
  2. <Components>
  3. <Label x="10" y="5" w="80" h="20" style="prompt" content="WEL_LANG"
  4. alignment="Left" opaque="true"/>
  5. <Combo name="languageList" x="90" y="5" w="120" h="20"/>
  6. <Label x="130" y="84" w="350" h="60" style="Heading" content="WEL_TEXT"
  7. alignment="Left" opaque="true"/>
  8. <Panel x="0" y="150" w="650" h="50" style="banner/prompt">
  9. <RadioButton name="soleRadio" x="226" y="15" w="100" h="20" style="banner/prompt"
  10. content="WEL_SOLE" alignment="Leading"/>
  11. <RadioButton name="jointRadio" x="330" y="15" w="100" h="20" style="banner/prompt"
  12. content="WEL_JOINT" alignment="Leading"/>
  13. </Panel>
  14. </Components>
  15. ...

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

Extract from en.properties

  1. WEL_LANG=Language
  2. WEL_TEXT=Welcome to ABC Bank's mortgage application.
  3. Before we proceed please note the following...
  4. WEL_SOLE=Sole
  5. WEL_JOINT=Joint
  6. BAN_TITLE=ABC Bank Mortgage Application
  7. BAN_CLOSE=Close Application
  8. PER_HEADING_1=Personal Details (First Applicant)
  9. PER_HEADING_2=Personal Details (Second Applicant)
  10. ...

And the French translation:

Extract from fr.properties

  1. WEL_LANG=Langue
  2. WEL_TEXT=Bienvenue ? la demande de pr?t hypoth?caire de l'hypoth?que de la banque de ABC.
  3. Avant que nous veuillez proc?der note le suivant...
  4. WEL_SOLE=Unique
  5. WEL_JOINT=Joint
  6. BAN_TITLE=Demande de pr?t hypoth?caire D'Hypoth?que De Banque de ABC
  7. BAN_CLOSE=Application ?troite
  8. PER_HEADING_1=D?tails Personnels (Premier Demandeur)
  9. PER_HEADING_2=D?tails Personnels (Deuxi?me Demandeur)
  10. ...

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.

Language switching code

  1. public void changeLanguage()
  2. {
  3. if ( langClicked ) {
  4. String lang = ( String )languageList.getSelectedObject();
  5. pageMgr.reset();
  6. String langCode = lang.compareTo( "English" ) == 0 ? "en" : "fr";
  7. project.setStartupParam( "Language", langCode );
  8. pageMgr.loadFrames( "frames", true );
  9. }
  10. }

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 Page and returns the result of it. The custom validation is shown below.

The custom FunctionValidation factory

  1. public class FunctionValidation extends BaseValidator {
  2.  
  3. public FunctionValidation() {
  4. }
  5.  
  6. public void validate( Object c, boolean forceMandatory ) throws Exception {
  7. Integer ret = ( Integer ) invokeMethod();
  8. if ( ret.intValue() > LEVEL_IGNORE ) {
  9. errorLevel = ret.intValue();
  10. throwException();
  11. }
  12. }
  13.  
  14. public void setup( XmlElement element )
  15. {
  16. message = element.getAttribute( "msg" );
  17. super.setup( element );
  18. }
  19. }

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

The custom ValidationFactory class

  1. public class ValidationFactory extends ValidationFactory {
  2.  
  3. ...
  4.  
  5. public Validator getValidation( String validationName, Method m, int mask, Object page )
  6. {
  7. XmlElement ele = ( XmlElement ) validations.get( validationName );
  8.  
  9. String type = ele.getAttribute( "type" );
  10.  
  11. if ( type.compareTo( "function" ) == 0 ) {
  12. FunctionValidation validator = new FunctionValidation();
  13. validator.setName( validationName );
  14. validator.setValidationMethod( m, page );
  15. validator.setup( ele );
  16. return validator;
  17. }
  18. else {
  19. BaseValidator validator = (BaseValidator)super.getValidation( validationName,
  20. mask, page );
  21. validator.setup( ele );
  22. return validator;
  23. }
  24. }
  25.  
  26. }

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 Aria to use this validation factory the following line needed to be added to the startup properties.

Startup property for the custom validation factory

  1. ValidationFactory=org.formaria.mortgage.validation.ValidationFactory

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

XML declaration for the function validation

  1. <Validation rule="createApp" target="jointRadio" method="checkCreateSetup"/>

And the welcome class was amended to include the checkCreateSetup function

The checkCreateSetup function

  1. ...
  2. public Integer checkCreateSetup()
  3. {
  4. int level = BaseValidator.LEVEL_IGNORE;
  5. if ( !jointRadio.isSelected() &amp;&amp; !soleRadio.isSelected() )
  6. level = BaseValidator.LEVEL_ERROR;
  7.  
  8. return new Integer( level );
  9. }
  10. ...

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.

Binding to the list component on the welcome page

  1. ...
  2. <List name="fileList" x="0" y="0" w="200" h="100" style="data"/>
  3. ...
  4. <Bind target="fileList" source="applications" output="temp/filename"/>
  5. ...

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

Loading files into the model

  1. public void pageCreated()
  2. {
  3. ...
  4. loadFiles();
  5. }
  6. ...
  7. private void loadFiles()
  8. {
  9. String dir = System.getProperty( "user.dir" );
  10. appsMdl = ( BaseModel )rootModel.get( "applications" );
  11. File folder = new File( dir + ";;files" );
  12. File files[] = folder.listFiles();
  13. for ( int i = 0; i < files.length; i++ ) {
  14. String name = files[ i ].getName();
  15. BaseModel filMdl = new BaseModel ( appsMdl, name, name );
  16. }
  17. updateBindings();
  18. }
  19. ...

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.

Opening and restoring the model state

  1. ...
  2. public void openFile()
  3. {
  4. String filename = ( String )fileList.getSelectedObject();
  5.  
  6. // Not shown here but simply reads the file into a string.
  7. String contents = getFileContents( filename );
  8. restoreState( contents );
  9.  
  10. updateBindings();
  11. }
  12.  
  13. private void restoreState( String contents )
  14. {
  15. ( ( BaseModel )rootModel.get( "aria_state/mortapp" ) ).clear();
  16. ( ( BaseModel )rootModel.get( "mortapp" ) ).clear();
  17.  
  18. StringReader sr = new StringReader( contents );
  19. XmlElement ele = XmlSource.read( sr );
  20.  
  21. if ( ele != null ) {
  22. OptionalDataSource ds = new OptionalDataSource();
  23. ds.loadTable( ele, rootModel );
  24. ds.loadTable( ele, ( ( BaseModel )rootModel.get( "aria_state" ) ) );
  25. }
  26.  
  27. }
  28. ...

The restoreState function is really of most importance here. It initialises the aria_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 Aria. To do this the navPanel page was changed to reference functions in the Navigator class as follows.

The amended MouseHandler events in the navPanel page

  1. <Events>
  2. <Event method="org.formaria.mortgage.navigate.Navigator.next"
  3. target="nextButton" type="MouseHandler"/>
  4. <Event method="org.formaria.mortgage.navigate.Navigator.previous"
  5. target="prevButton" type="MouseHandler"/>
  6. </Events>

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.

The navigation.xml file

  1. <dataset id="navigation">
  2. <page id="welcome" value="welcome" />
  3. <page id="personal" value="personal" startrepeat="true" title="PER_HEADING"/>
  4. <page id="bank" value="bank" endrepeat="true" title="BANK_HEADING"/>
  5. <page id="finance" value="finance" title="FINANCE_TITLE"/>
  6. <page id="finish" value="finish" />
  7. </dataset>

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.

The generic CustomerPage class

  1. public class CustomerPage extends Page
  2. {
  3. public String getCustomerID()
  4. {
  5. return "customer" + Navigator.getInstance().getCurrentCust();
  6. }
  7.  
  8. }

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.

The new declaration in frames.xml

  1. <FrameSet>
  2. <Frame name="banner" constraint="NORTH" content="banner"
  3. width="640" height="96"/>
  4. <Frame name="appstatus" constraint="WEST" content="appstatus"
  5. width="150" height="500" />
  6. <Frame name="content" constraint="CENTER" content="welcome"
  7. width="490" height="288" />
  8. <Frame name="navPanel" constraint="SOUTH" content="navPanel"
  9. width="640" height="96"/>
  10. </FrameSet>

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.

The components.xml file with the declaration for NavViewer

  1. <Components>
  2. <Component name="NavViewer" class="org.formaria.mortgage.comp.NavViewer">
  3. <Property name="Style" mode="Normal" type="both"/>
  4. <Property name="DefaultStyle" mode="Normal" type="both"/>
  5. <Property name="CurrentStyle" mode="Normal" type="both"/>
  6. <Property name="DoneStyle" mode="Normal" type="both"/>
  7. <Property name="NavModelPath" mode="Normal" type="both"/>
  8. </Component>
  9. </Components>

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

The AppStatus.xml file

  1. <Page class="org.formaria.mortgage.AppStatus" style="Heading" resource="">
  2. <Components>
  3. <NavViewer name="navViewer" x="0" y="0" w="145" h="400" Style="status"
  4. DefaultStyle="status/default" CurrentStyle="status/current"
  5. DoneStyle="status/done" NavModelPath="navigation"/>
  6. <Label x="149" y="0" w="1" h="500" style="banner" opaque="true"/>
  7. </Components>
  8. </Page>

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.

The ExceptionHandler class

  1. public boolean handleException( Object comp, Exception ex, Object xvalidator )
  2. {
  3. ...
  4. if ( validator.getLevel() == validator.LEVEL_ERROR )
  5. errors.add( currentPage.translate( msg ) );
  6. else
  7. warnings.add( currentPage.translate( msg ) );
  8.  
  9. return true;
  10. }
  11.  
  12. public int accumulateMessages( boolean start, int level )
  13. {
  14. pageValidation = start;
  15. if ( pageValidation ){
  16. errors = new Vector();
  17. warnings = new Vector();
  18. } else {
  19. if ( errors.size() > 0 || warnings.size() > 0 )
  20. return getDialogResult( level );
  21. }
  22. return level;
  23. }
  24.  
  25.  
  26.  
  27. private int getDialogResult( int level )
  28. {
  29. ErrorMessage dlg =
  30. (ErrorMessage)ProjectManager.getPageManager().loadPage( "ErrorDlg", false );
  31. dlg.setErrorMessages( errors, warnings );
  32. int result = dlg.showDialog( currentPage );
  33. if ( level == Validator.LEVEL_WARNING )
  34. return result == Validator.LEVEL_IGNORE ? Validator.LEVEL_IGNORE : level;
  35. else
  36. return Validator.LEVEL_ERROR;
  37. }

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.

The ErrorMessage class

  1. ...
  2. public int showDialog( Container owner )
  3. {
  4. super.showDialog( owner );
  5. return ret;
  6. }
  7.  
  8. public void cancelClicked()
  9. {
  10. ret = BaseValidator.LEVEL_WARNING;
  11. super.closeDlg();
  12. }
  13.  
  14. public void okClicked()
  15. {
  16. ret = BaseValidator.LEVEL_IGNORE;
  17. super.closeDlg();
  18. }
  19. ...

Case Study: Wizards

  1. Wizard application

Introduction

This wizard application which will be examined in this section is for generating product selection applications for SMEs. It is a fully functional application and is in use in production environments by some of Formaria's clients. It's intended audience is small to medium companies who wish to manage their product listings and to make those products available within a simple and easy to use application. The typical user would not be particularly technical and with that in mind spreadsheets were chosen as the preferred format for storing product information. The wizard is only the first step in creating the application for the end user and can be used to setup and manage any number of projects.

This application makes use of many of the features of Aria including page navigation, framesets, component registration, localization, validations, the synth look and feel and data binding amongst others.

Framesets and pages

The application consists of a Aria frameset which contains four different target areas. The title frame and navigation frame both remain in the same position with the same content throughout the lifetime of the application. A status frame on the left is initially hidden and is then shown when the user decides on a wizard route. The main content frame takes up the full width of the application when it is first started and contains the WelcomePage Page. Once the status frame appears the width of the main content page is the width of the application less the width of the status frame. The frameset file is defined below.

frames.xml

  1. <FrameSet>
  2. <Frame name="banner" constraint="NORTH" content="banner" width="640"
  3. height="70"/>
  4. <Frame name="wizardstatus" constraint="WEST" content="wizardstatus"
  5. width="150" height="500"/>
  6. <Frame name="content" constraint="CENTER" content="welcome"
  7. width="490" height="324"/>
  8. <Frame name="navPanel" constraint="SOUTH" content="navPanel"
  9. width="640" height="70"/>
  10. </FrameSet>

From the welcome page, it is possible to select one of four radio buttons which will determine the route through the wizard.

The four radio buttons and their declared events

  1. ...
  2. <RadioButton name="createRad" x="150" y="100" w="250" h="20"
  3. style="welcome/prompt" content="WEL_CREATE_PROJ"/>
  4. <RadioButton name="generateRad" x="150" y="120" w="250" h="20"
  5. style="welcome/prompt" content="WEL_GEN_PROJ"/>
  6. <RadioButton name="buildRad" x="150" y="140" w="250" h="20"
  7. style="welcome/prompt" content="WEL_BUILD_PROJ"/>
  8. <RadioButton name="hotspotRad" x="150" y="160" w="250" h="20"
  9. style="welcome/prompt" content="WEL_EDIT_HOTSPOT"/>
  10. <Label name="infoLbl" x="100" y="190" w="300" h="80"
  11. style="Infotext" alignment="Center" opaque="false"/>
  12. ...
  13. <Event target="createRad" type="ItemHandler" method="showInfo"/>
  14. <Event target="generateRad" type="ItemHandler" method="showInfo"/>
  15. <Event target="buildRad" type="ItemHandler" method="showInfo"/>
  16. <Event target="hotspotRad" type="ItemHandler" method="showInfo"/>
  17. ...

The XML shows the four declarations for each of the radio buttons. The application is completely localized so the content attribute is set to the key for the current resource bundle. The four event declarations are all setup to call the same method showInfo which is shown below. There is also a declaration for a label which will show information for each of the selections and, again, this component uses the current language resource bundle to populate its text. So now the information label needs to be populated depending on the selected radio button.

The Welcome class

  1. ...
  2. RadioButton createRad, generateRad, buildRad, hotspotRad;
  3. Label infoLbl;
  4.  
  5. public void pageCreated() {
  6. createRad = ( RadioButton )findComponent( "createRad" );
  7. generateRad = ( RadioButton )findComponent( "generateRad" );
  8. buildRad = ( RadioButton )findComponent( "buildRad" );
  9. hotspotRad = ( RadioButton )findComponent( "hotspotRad" );
  10. infoLbl = ( Label ) findComponent( "infoLbl" );
  11. ...
  12. }
  13.  
  14. public void showInfo()
  15. {
  16. NavPanel navPanel = ( NavPanel ) pageMgr.getPage( "navPanel" );
  17. String text = "";
  18. if ( createRad.isSelected() ) {
  19. text = "WEL_CREATE_TEXT";
  20. navPanel.setNavPath( "wizard" );
  21. } else if ( generateRad.isSelected() ) {
  22. text = "WEL_GEN_TEXT";
  23. navPanel.setNavPath( "generator" );
  24. } else if ( buildRad.isSelected() ) {
  25. text = "WEL_PACK_TEXT";
  26. navPanel.setNavPath( "builder" );
  27. } else if ( hotspotRad.isSelected() ) {
  28. text = "WEL_HOTSPOT_GEN";
  29. navPanel.setNavPath( "hotspot" );
  30. }
  31. infoLbl.setText( Translator.translate( text ) );
  32. itemSelected = true;
  33. }
  34. ...

The pageCreated method obtains references to the components which will be used in this operation and the showInfo function sets the translated text in the label for the selected radio button. This application uses a specialized Translator class which will be explained further on.

The NavPanel class is the navigation Page which was declared last in the frames.xml file above. The setNavPath call sets the navigation path which needs to be taken for the selected radio button and that will be looked at next.

Navigation

The application consists of multiple pages and navigation between pages is provided by a common class. In the screenshot below the page navigation is initiated by the buttons at the bottom right of the page.

The NavPanel class controls the navigation of the wizard and is shown below.

The NavPanel class

  1. public class NavPanel extends BasePage {
  2.  
  3. Button prevBtn, nextBtn;
  4. BasePage currentPage;
  5. NavigationManager wizardNavigator, generatorNavigator, builderNavigator,
  6. hotspotNavigator, currentNavigator;
  7.  
  8. public void pageCreated()
  9. {
  10. prevBtn = ( Button ) findComponent( "prevBtn" );
  11. nextBtn = ( Button ) findComponent( "nextBtn" );
  12. setCursors();
  13. wizardNavigator = new WizardNavigator( nextBtn, prevBtn, "navigation/wizardpages" );
  14. generatorNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/generationpages" );
  15. builderNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/builderpages" );
  16. hotspotNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/hotspotpages" );
  17. currentNavigator = wizardNavigator;
  18. }
  19.  
  20. public void setNavPath( String name )
  21. {
  22. if ( name.compareTo( "wizard" ) == 0 )
  23. currentNavigator = wizardNavigator;
  24. else if ( name.compareTo( "generator" ) == 0 )
  25. currentNavigator = generatorNavigator;
  26. else if ( name.compareTo( "builder" ) == 0 )
  27. currentNavigator = builderNavigator;
  28. else if ( name.compareTo( "hotspot" ) == 0 )
  29. currentNavigator = hotspotNavigator;
  30. }
  31.  
  32. private void setCursors()
  33. {
  34. prevBtn.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
  35. nextBtn.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
  36. }
  37.  
  38. public void prevPage()
  39. {
  40. currentNavigator.showPrevPage();
  41. }
  42.  
  43. public void nextPage()
  44. {
  45. currentNavigator.showNextPage();
  46. }
  47.  
  48. public void goHome()
  49. {
  50. currentNavigator.showHomePage();
  51. }
  52.  
  53. public void home()
  54. {
  55. goHome();
  56. }
  57. }

The pages of this application are using a custom BasePage class which is derived from the Page. The reasons for doing this are irrelevant to this discussion. The setNavPath function which is called from the Welcome Page can be seen. This function assigns one of four NavigationManager instances to the currentNavigator variable. Each of the NavigationManagers accepts the next and previous buttons as contructor arguments along with the path to be followed. This allows the NavigationManagers to control the enabled or visible state of the buttons depending on where the current page of the application.

The navigation paths should be explained now. This is the path into some predefined nodes in the datamodel which can be seen in the wizardpages.xml file below. This page needs to be referenced from the datasource file as referred to by the ModelData startup property.

wizardpages.xml

  1. <Datasets>
  2. <dataset id="navigation">
  3. <dataset id="wizardpages">
  4. <data value="welcome"/>
  5. <data value="projectinit" desc="WIZ_PROJ_SET"/>
  6. <data value="languagepage" desc="WIZ_PROJ_LANG"/>
  7. <data value="colourscheme" desc="WIZ_PROJ_LAYOUT"/>
  8. <data value="categories" desc="WIZ_PROJ_CATS"/>
  9. <data value="categorysetup" desc="WIZ_PROJ_CAT_DET"/>
  10. <data value="promptspage" desc="WIZ_PROJ_PROMPTS"/>
  11. <data value="constantspage" desc="WIZ_PROJ_CNST"/>
  12. <data value="finish" desc="WIZ_PROJ_FINISH"/>
  13. </dataset>
  14.  
  15. <dataset id="generationpages">
  16. <data value="welcome"/>
  17. <data value="projectgenerate" desc="WIZ_GEN"/>
  18. </dataset>
  19.  
  20. <dataset id="builderpages">
  21. <data value="welcome"/>
  22. <data value="projectbuilder" desc="WIZ_BUILD"/>
  23. </dataset>
  24.  
  25. <dataset id="hotspotpages">
  26. <data value="welcome"/>
  27. <data value="hotspotsetup" desc="WIZ_HTSPT_SPEC"/>
  28. <data value="hotspotedit" desc="WIZ_HTSPT_EDIT"/>
  29. </dataset>
  30. </dataset>
  31. </Datasets>

Now the NavigationManager can take care of displaying the correct page depending on whether the next or previous button was clicked.

NavigationManager class

  1. public class NavigationManager {
  2.  
  3. BaseModel navMdl;
  4. protected int currentNode = 0;
  5. BasePage currentPage;
  6. Button nextBtn, prevBtn;
  7. WizardStatus wizStatus;
  8. PageManager pageMgr;
  9.  
  10. public NavigationManager(
  11. Button nextBtn,
  12. Button prevBtn,
  13. String navPath ) {
  14. Project project = ProjectManager.getCurrentProject();
  15. pageMgr = project.getPageManager();
  16. navMdl = ( BaseModel )project.getModel().get( navPath );
  17. this.nextBtn = nextBtn;
  18. this.prevBtn = prevBtn;
  19. wizStatus = ( WizardStatus )pageMgr.showPage( "wizardstatus",
  20. "wizardstatus" );
  21. setWizardPanelSize( 0 );
  22. }
  23.  
  24. public void showPrevPage()
  25. {
  26. nextBtn.setVisible(true);
  27. currentNode--;
  28. showpage();
  29. if (currentNode == 0)
  30. prevBtn.setVisible(false);
  31. }
  32.  
  33. public void showNextPage()
  34. {
  35. if ( currentPage == null ) {
  36. BaseModel mdl = (BaseModel) navMdl.get( currentNode );
  37. String pagename = (String) mdl.get();
  38. currentPage = (BasePage) pageMgr.getPage( pagename );
  39. wizStatus.setNavModel( navMdl );
  40. }
  41. if ( currentPage.checkValidations() == Validator.LEVEL_IGNORE ) {
  42. prevBtn.setVisible(true);
  43. currentNode++;
  44. showpage();
  45. if (currentNode == ( navMdl.getNumChildren() - 1 ) )
  46. nextBtn.setVisible(false);
  47. }
  48. }
  49.  
  50. public void showHomePage()
  51. {
  52. currentPage = null;
  53. currentNode = 0;
  54. nextBtn.setVisible( true );
  55. prevBtn.setVisible( false );
  56. BaseModel mdl = (BaseModel) navMdl.get( currentNode );
  57. String pagename = (String) mdl.get();
  58. currentPage = (BasePage) pageMgr.showPage( pagename );
  59. setWizardPanelSize( 0 );
  60. }
  61. private void showpage()
  62. {
  63. BaseModel mdl = ( BaseModel ) navMdl.get( currentNode );
  64. String pagename = ( String ) mdl.get();
  65. currentPage = ( BasePage ) pageMgr.showPage( pagename );
  66. setWizardPanelSize( 100 );
  67. wizStatus.setCurrentItem( currentNode );
  68. }
  69. protected void setWizardPanelSize( int width )
  70. {
  71. Container c = wizStatus.getParent();
  72. c.setVisible( width == 0 ? false : true );
  73. c.getParent().doLayout();
  74. }
  75. }

The constructor obtains a reference to the model node specified by the path parameter. This node contains all of the child nodes which define the pages for this wizard route. The constructor also obtains a reference to the wizardstatus page which is declared second in the frames.xml file. and sets its width to zero and also makes it invisible.

The showNextPage function first checks to see if the currentPage is null. If it is null it retrieves the name of the first page node in the navMdl and passes the navMdl node to the WizardStatus page. It then checks the currentpage for validations and if the validations pass the previous button is set to visible and the next page in the route is displayed. If the page displayed is the last page in the route the next button is made invisible.

The showPage function takes care is displaying the current page, making the WizardStatus page visible and passing the current node to the WizardStatus page.

The showHomePage function simply resets the variables and sets the appropriate visibility for the navigation buttons.

The WizardStatus page can now be looked at.

The WizardStatus class

  1. public class WizardStatus extends BasePage {
  2.  
  3. int currentNode = -1;
  4. BaseModel navMdl;
  5.  
  6. /** Creates a new instance of WizardStatus */
  7. public WizardStatus() {
  8. }
  9.  
  10. public void setNavModel( BaseModel mdl )
  11. {
  12. navMdl = mdl;
  13. repaint();
  14. }
  15.  
  16. public void setCurrentItem( int item )
  17. {
  18. currentNode = item;
  19. repaint();
  20. }
  21.  
  22. public void paint( Graphics g )
  23. {
  24. super.paint( g );
  25. g.setColor( new Color( 136, 136, 136 ) );
  26. if ( navMdl != null ) {
  27. int currentY = 50;
  28. for ( int i = 1; i < navMdl.getNumChildren(); i++ ) {
  29. if ( i == currentNode ) {
  30. g.setColor( new Color( 255, 58, 0 ) );
  31. } else if ( i > currentNode ) {
  32. g.setColor( new Color( 136, 136, 0 ) );
  33. }
  34. BaseModel itemMdl = ( BaseModel ) navMdl.get( i );
  35. String text = Translator.translate(
  36. ModelHelper.getAttrib( itemMdl, "desc" ));
  37. g.drawString( text, 10, currentY );
  38. currentY += 20;
  39. }
  40. }
  41. }
  42. }

The setNavModel function as referred to from the NavigationManager class can be seen taking the path node as a parameter. The setCurrentItem function stores the index of the current page in the path in a class variable. The paint function then simply iterates all of the page nodes in the navigation model and outputs the translated description of the page at intervals of twenty pixels. When the current node is reached the color of the text is changed to indicate the progress of the wizard.

One final scenario needs to be mentioned before leaving this topic. The WizardNavigator as defined in the NavPanel class is derived from the NavigationManager class and needs to do some specialized navigation. This navigation class overloads some of the NavigationManager functions in order to repeat specific pages in the navigation path. There is little need to go into the specifics of that navigation here but it's worth noting to show how custom NavigationManagers can be created.

Component Registration

There are several custom components included in the wizard application each designed to carry out a specific task. This section will look at a single component, the FileLocater, which can be seen in the screenshot above. There are two of these components on the first screen on the main navigation path to specify the project location and the icon path.

FileLocator constructor, setAttribute and actionPerformed methods

  1. public class FileLocater extends JPanel
  2. implements AttributedComponent, ActionListener, TextHolder
  3. {
  4. StyleFactory fact;
  5. Label promptLbl;
  6. Edit fileEdit;
  7. Button locateBtn;
  8.  
  9. public FileLocater() {
  10. setLayout( null );
  11. fact = new StyleFactory( ProjectManager.getCurrentProject(),
  12. "org.formaria.swing" );
  13. fact.setParentComponent( this );
  14. promptLbl = ( Label ) fact.addComponent( "LABEL", 0, 0, 30, 20 );
  15. fileEdit = ( Edit ) fact.addComponent( "EDIT", 30, 0, 30, 20 );
  16. locateBtn = ( Button ) fact.addComponent( "BUTTON", 0, 0, 30, 20, "..." );
  17. locateBtn.addActionListener( this );
  18. }
  19.  
  20. public void setAttribute( String name, Object objValue )
  21. {
  22. String value = ( String )objValue;
  23. if ( name.compareTo( "content" ) == 0 )
  24. setContent( value );
  25. else if ( name.compareTo( "promptWidth" ) == 0 )
  26. setPromptWidth( value );
  27. else if ( name.compareTo( "dataStyle" ) == 0 )
  28. setDataStyle( value );
  29. else if ( name.compareTo( "style" ) == 0 )
  30. fact.applyStyle( promptLbl, value );
  31. }
  32.  
  33. public void actionPerformed( ActionEvent ae )
  34. {
  35. JFileChooser dlg = new JFileChooser( fileEdit.getText() );
  36. dlg.setFileSelectionMode(
  37. javax.swing.JFileChooser.FILES_AND_DIRECTORIES);
  38. int returnVal = dlg.showOpenDialog( this );
  39.  
  40. if(returnVal == JFileChooser.APPROVE_OPTION) {
  41. fileEdit.setText( dlg.getSelectedFile().getAbsolutePath() );
  42. }
  43. }
  44.  
  45. public void setContent( String value )
  46. {
  47. promptLbl.setText( Translator.translate( value ) );
  48. }
  49.  
  50. public void setPromptWidth( String value )
  51. {
  52. promptLbl.setSize( Integer.parseInt( value ), promptLbl.getSize().width );
  53. doLayout();
  54. }
  55.  
  56. public void setDataStyle( String value )
  57. {
  58. fact.applyStyle( fileEdit, value );
  59. }

The constructor sets the layout of the panel to null. It then constructs a new StyleFactory which will use the org.formaria.swing widget set to construct it's components. The factory is then used to create the label, text box and the locate button. An action listener is added to the button. The setAttribute function as defined in the AttribedComponent interface is called from the AriaBuilder when an unknown component attribute is found. The attributes being looked for by the component are content, promptWidth, dataStyle and style. Each of these properties are set using the component functions. It can be seen how the StyleFactory can apply a style to a component with the simple applyStyle call.

The actionPerformed function shows a JFileChooser being created with it's default path set to the text of the text component. If a new path is selected by the user the text component is once more set.

The remainder of the component can now be completed as below.

Remainder of the FileLocater class

  1. public void doLayout()
  2. {
  3. fileEdit.setLocation( promptLbl.getSize().width, 0 );
  4. fileEdit.setSize( getWidth() - promptLbl.getWidth() - 30, 20 );
  5. locateBtn.setLocation( getWidth() - 30, 0 );
  6. setOpaque( false );
  7. super.doLayout();
  8. }
  9.  
  10. public String getText()
  11. {
  12. return fileEdit.getText();
  13. }
  14.  
  15. public void setText( String text )
  16. {
  17. fileEdit.setText( text == null ? "" : text );
  18. }
  19.  
  20. public File getFile()
  21. {
  22. return new File( getText() );
  23. }
  24.  
  25. public Edit getEditComponent()
  26. {
  27. return fileEdit;
  28. }
  29.  
  30. public void setBackground( Color c )
  31. {
  32. super.setBackground( null );
  33. super.setOpaque( false );
  34. }

The doLayout function sets the location of the text and button components relative to the width of the text component and sets the panel to be transparent.

This component can now be declared in a component registration file as shown below.

components.xml

  1. <Components>
  2. <Component class="org.formaria.quotation.FileLocater"
  3. name="FileLocater">
  4. <Property type="set" name="content" />
  5. <Property type="set" name="promptWidth" />
  6. <Property type="set" name="dataStyle" />
  7. </Component>
  8. ...
  9. <Components>

The following startup properties need to be set. The RegisteredComponentFactory will assume a filename of components.xml

startup properties

  1. NumComponentFactories=2
  2. ComponentFactory0=org.formaria.swing.SwingComponentFactory
  3. ComponentFactory1=org.formaria.registry.RegisteredComponentFactory

The custom component is now ready to be used within the Aria pages. This can now be done very simply by creating an element whose name corresponds to the definition of the component in the components.xml file.

ProjectInit XML page declaration

  1. <Components>
  2. <FileLocater name="pathEdit" x="10" y="10" w="430" h="20"
  3. content="PROJINIT_PROJ_DIR" promptWidth="160"
  4. style="base/prompt" dataStyle="base/data"/>
  5. <FileLocater name="iconEdit"
  6. x="10" y="30" w="430" h="20"
  7. content="PROJINIT_PROJ_ICON" promptWidth="160"
  8. style="base/prompt" dataStyle="base/data"/>
  9. ...

The location, dimension and name attributes are used as usual and the custom attributes as expected by the setAttribute function can be seen. This example gives an impression of how the StyleManager and styles in general can be used to customize the application in general.

Validations

This application requires the use of custom validations which go beyond the abilities of the basic validations built into the Aria core libraries. This requires the use of a custom validation factory which can be specified in the startup properties file as below.

Specifying the custom validation factory

  1. ...
  2. ValidationFactory=org.formaria.quotation.validation.ValidationFactory
  3. ...

The custom validation factory is shown below.

The ValidationFactory class

  1. public class ValidationFactory extends ValidationFactory {
  2.  
  3. public ValidationFactory( Project project )
  4. {
  5. super( project );
  6. }
  7.  
  8. public ValidationFactory( Reader reader ) {
  9. super ( reader );
  10. }
  11.  
  12. public Validator getValidation( String validationName, Method m,
  13. int mask, Object page, XmlElement pageEle )
  14. {
  15. XmlElement ele = ( XmlElement ) validations.get( validationName );
  16. if ( BuildProperties.DEBUG ) {
  17. if ( ele == null ) {
  18. DebugLogger.logError( "Cannot find the validation rule: " + validationName );
  19. return null;
  20. }
  21. }
  22.  
  23. String type = ele.getAttribute( "type" );
  24.  
  25. if ( type.compareTo( "integer" ) == 0 ) {
  26. IntegerValidation validator = new IntegerValidation();
  27. validator.setName( validationName );
  28. validator.setMask( mask );
  29. validator.setup( ele );
  30. return validator;
  31. }
  32. else if ( type.compareTo( "number" ) == 0 ) {
  33. NumberValidation validator = new NumberValidation();
  34. validator.setName(validationName);
  35. validator.setMask(mask);
  36. validator.setup(ele);
  37. return validator;
  38. }
  39. else if ( type.compareTo( "function" ) == 0 ) {
  40. FunctionValidation validator = new FunctionValidation();
  41. validator.setName( validationName );
  42. validator.setup( ele );
  43. return validator;
  44. }
  45. else {
  46. BaseValidator validator = ( BaseValidator ) super.getValidation(
  47. validationName, m, mask, page pageEle );
  48. validator.setup( ele );
  49. return validator;
  50. }
  51. }
  52.  
  53. }

This custom ValidationFactory class is aware of three types of custom validations, integer , number and function . If the validation being created is none of these then the super getValidation function is called in order to create the validation and is returned. Two of the custom validations are shown below. The first:

The NumberValidation class

  1. public class NumberValidation extends BaseValidator {
  2. public NumberValidation( Project project )
  3. {
  4. super( project );
  5. }
  6.  
  7. public void validate( Object c, boolean forceMandatory )
  8. throws java.lang.Exception
  9. {
  10. if ( forceMandatory ) {
  11. String text = getText(c);
  12. try {
  13. Double.parseDouble( text );
  14. }
  15. catch ( Exception ex ) {
  16. errorLevel = LEVEL_ERROR;
  17. throwException();
  18. }
  19. }
  20. }
  21.  
  22. }

And the second:

The FunctionValidation class

  1. public class FunctionValidation extends BaseValidator {
  2.  
  3. public FunctionValidation( Project project )
  4. {
  5. super( project );
  6. }
  7.  
  8. public void validate( Object c, boolean forceMandatory )
  9. throws java.lang.Exception
  10. {
  11. Integer ret = ( Integer ) invokeMethod();
  12. if ( ret.intValue() > LEVEL_IGNORE ) {
  13. errorLevel = ret.intValue();
  14. throwException();
  15. }
  16. }
  17.  
  18. public void setup( XmlElement element )
  19. {
  20. String function = element.getAttribute( "method" );
  21. super.setup( element );
  22. }
  23.  
  24. }

Both classes extend the BaseValidator class and overload the validate method. The NumberValidation class simply attempts to parse the component text and if that fails the errorLevel variable of the super class is set and an exception is thrown. The FunctionValidation class overloads the setup function and retrieves the name of the method which needs to be called in order to carry out the validation.

The validations need to be declared in the validations.xml file some of which are shown below.

Validations.xml

  1. <validation name="overwriteFile" type="function"
  2. msg="VAL_OVERWRITE_FILE"/>
  3. <validation name="rate" type="number"
  4. msg="VAL_QTY_NUM" mandatory="true"/>
  5. ...

And the validation rules themselves are as follows:

The validations defined in a page

  1. <Validation rule="rate" target="taxEdit"/>
  2. <Validation rule="overwriteFile" target="finishBtn"
  3. method="fileExists"/>

The rate validation checks that the text entered into the taxEdit text component is numeric. The overwriteFile function calls the fileExists function in the Page as shown below.

fileExists function

  1. public Integer fileExists()
  2. {
  3. File f = new File( getPath() + File.separatorChar + "xlintquote.xls" );
  4. return new Integer( f.exists() ?
  5. BaseValidator.LEVEL_WARNING : BaseValidator.LEVEL_IGNORE );
  6. }

The fileExists function checks for the existence of a named file and if exists the BaseValidator LEVEL_WARNING is returned in an Integer object. If it does not exist the LEVEL_IGNORE value is returned.

One of the functions of the BasePage class is to set a custom ExceptionHandler for each of the pages as shown below.

BasePage constructor

  1. public class BasePage extends Page implements ModelListener {
  2.  
  3. public BasePage()
  4. {
  5. setExceptionHandler( new ExceptionHandler( this ) );
  6. }

The custom exception handler follows. The ExceptionHandler class

  1. public class ExceptionHandler implements ExceptionHandler
  2. {
  3. boolean pageValidation = false;
  4. String validationText = "";
  5. private Page currentPage;
  6. Vector errors, warnings;
  7.  
  8. public ExceptionHandler( Page page )
  9. {
  10. currentPage = page;
  11. }
  12.  
  13. public boolean handleException( Object comp, Exception ex,
  14. Object xvalidator )
  15. {
  16. Validator validator = ( Validator ) xvalidator;
  17. if ( ( validator.getLevel() == validator.LEVEL_ERROR ) &&
  18. ( ! pageValidation ) ){
  19. currentPage.showMessage( "Input error", ex.getMessage() );
  20. return true;
  21. }
  22.  
  23. String msg = validator.getMessage();
  24. if ( validationText.length()>0 )
  25. validationText += "&#92;n";
  26. validationText += msg;
  27.  
  28. if ( validator.getLevel() == validator.LEVEL_ERROR )
  29. errors.add( msg );
  30. else
  31. warnings.add( msg );
  32.  
  33. return true;
  34. }
  35.  
  36. public int accumulateMessages( boolean start, int level )
  37. {
  38. pageValidation = start;
  39. if ( pageValidation ){
  40. errors = new Vector();
  41. warnings = new Vector();
  42. validationText = "";
  43. }
  44.  
  45. else {
  46. if ( validationText.length()>0 ) {
  47. if ( level == Validator.LEVEL_WARNING ) {
  48. ErrorMessage dlg = new ErrorMessage( errors, warnings );
  49. dlg.setSaveOnClose( false );
  50. int r = dlg.showDialog( currentPage );
  51. return r == 0 ? 0 : level;
  52. }
  53.  
  54. else {
  55. ErrorMessage dlg = new ErrorMessage( errors, warnings );
  56. dlg.setSaveOnClose( false );
  57. dlg.showDialog( currentPage );
  58. return BaseValidator.LEVEL_ERROR;
  59. }
  60. }
  61. }
  62. return level;
  63. }
  64. }

The accumulateMessages function is called when the validations for a page begins and is passed true in the start parameter. This value is recorded in the pageValidation variable. The handleException function is called whenever a validation triggers a problem and depending on value returned from the getValue function of the Validator object the validation message is added to the appropriate Vector. When all of the validations are finished the accumulateMessages function is called once more and this time the start parameter is false . In this case an Dialog ErrorMessage is created in two possible ways. If there are errors the messages will be displayed with only a close button. If there are only warnings generated then the dialog will give the option of cancelling or continuing, disregarding the warnings. This functionality can be seen in the value returned from the showDialog function. This will be explored in more detail in the section on dialogs.

The custom FileLocater component needs to have several validations applied to it as shown below.

The FileLocator validations

  1. ...
  2. <Validation rule="projectpath" target="pathEdit"/>
  3. <Validation rule="projectexists" target="pathEdit" method="projectExists"/>
  4. <Validation rule="projectfolder" target="pathEdit" method="projectFolder"/>
  5. <Validation rule="folderexists" target="pathEdit" method="folderExists"/>
  6. ...

These validations are declared in the validations file as below.

The validations.xml declarations

  1. ...
  2. <validation name="projectpath" type="mandatory" msg="VAL_PATH_CANNOT_EMPTY"/>
  3. <validation name="projectexists" type="function" msg="VAL_PATH_NOT_EMPTY"/>
  4. <validation name="projectfolder" type="function" msg="VAL_NOT_DIR"/>
  5. <validation name="folderexists" type="function" msg="VAL_PATH_NOT_EXIST"/>
  6. ...</P>
  7. </TD>
  8. </TR>
  9. </CAPTION>
  10.  
  11. </TABLE>
  12. <P>
  13. The <EM>
  14. projectPath</EM>
  15. is a simple built in mandatory validation and the functions for the <EM>
  16. FunctionValidations</EM>
  17. follows.</P>
  18. <p><i>
  19. The FunctionValidations functions</i></p><java>
  20. public Integer projectExists()
  21. {
  22. File f = pathEdit.getFile();
  23. boolean ret = ( f.list() == null ) || ( f.list().length == 0 );
  24. return new Integer( ret ?
  25. BaseValidator.LEVEL_IGNORE : BaseValidator.LEVEL_WARNING );
  26. }
  27.  
  28. public Integer folderExists()
  29. {
  30. return new Integer( pathEdit.getFile().exists() ?
  31. BaseValidator.LEVEL_IGNORE : BaseValidator.LEVEL_ERROR );
  32. }
  33.  
  34. public Integer projectFolder()
  35. {
  36. return new Integer( pathEdit.getFile().isDirectory() ?
  37. BaseValidator.LEVEL_IGNORE : BaseValidator.LEVEL_ERROR );
  38. }

Data binding

The data bindings for the wizard application are on the whole pretty straightforward and there are some which deserve particular attention.

The projectinit page

  1. <Components>
  2. <Panel constraint="Center" painter="org.formaria.swing.LogoBackground"
  3. style="synthPanelLight">
  4. <Label x="20" y="10" w="500" h="30" style="base/title"
  5. content="PROJINIT_TITLE" opaque="false"/>
  6. <Panel x="100" y="100" w="450" h="250" border="1"
  7. painter="org.formaria.swing.LogoBackground"
  8. style="synthPanelLight">
  9. <FileLocater name="pathEdit" x="10" y="10" w="430" h="20"
  10. content="PROJINIT_PROJ_DIR"
  11. promptWidth="160" style="base/prompt" dataStyle="base/data"/>
  12. <FileLocater name="iconEdit" x="10" y="30" w="430" h="20"
  13. content="PROJINIT_PROJ_ICON"
  14. promptWidth="160" style="base/prompt" dataStyle="base/data"/>
  15. <Label x="10" y="50" w="160" h="20" style="base/prompt"
  16. content="PROJINIT_PROJ_TITLE"/>
  17. <Edit name="titleEdit" x="170" y="50" w="240" h="20"
  18. style="base/data"/>
  19. <Label x="10" y="70" w="160" h="20" style="base/prompt"
  20. content="PROJINIT_PROJ_WEL_TITLE"/>
  21. <Edit name="welcomeTitleEdit" x="170" y="70" w="240"
  22. h="20" style="base/data"/>
  23. <Label x="10" y="90" w="160" h="20" style="base/prompt"
  24. content="PROJINIT_PROJ_WEL_MES"/>
  25. <ScrollPane x="170" y="90" w="240" h="100">
  26. <TextArea name="welcomeText" x="0" y="0" w="250" h="100"
  27. style="base/data"/>
  28. </ScrollPane>
  29. <Label x="10" y="200" w="160" h="20" style="base/prompt"
  30. content="PROJINIT_PROJ_WEL_INFO"/>
  31. <Edit name="infoEdit" x="170" y="200" w="240" h="20"
  32. style="base/data"/>
  33. </Panel>
  34. </Panel>
  35. </Components>
  36. <Data>
  37. <Bind target="pathEdit" source="/project/projectPath"
  38. class="org.formaria.quotation.data.FileLoaderBinding"/>
  39. <Bind target="iconEdit" source="/project/projectIcon"
  40. class="org.formaria.quotation.data.FileLoaderBinding"/>
  41. <Bind target="titleEdit" source="/project/projectTitle"/>
  42. <Bind target="welcomeTitleEdit" source="/project/welcomeTitle"/>
  43. <Bind target="welcomeText" source="/project/welcomeMessage"/>
  44. <Bind target="infoEdit" source="/project/companyInfo"/>
  45. </Data>

The last four bindings are straightforward text bindings and are bound to text components. The first two bindings are bould to the custom FileLocator components created earlier. The type attribute specifies the use of a custom binding and the class attribute provides the name of the class which will carry out the binding. The FileLoaderBinding is shown below.

FileLoaderBinding class

  1. public class FileLoaderBinding extends TextBinding {
  2. public FileLoaderBinding() {
  3. }
  4.  
  5. public void setup( Project project, Object c,
  6. Hashtable bindingConfig, Hashtable instanceConfig )
  7. {
  8. comp = ( ( FileLocater ) c ).getEditComponent();
  9. srcPath = ( String )instanceConfig.get( "source" );
  10. BaseModel srcModel = ( BaseModel ) project.getModel().get( srcPath );
  11. outputPath = DataModel.prefixOutputPath( srcPath );
  12. if (( srcModel == null ) && ( srcPath != null ))
  13. sourceModel = ( DataModel )project.getModel().get( srcPath );
  14. else
  15. sourceModel = srcModel;
  16. comp = c;
  17. attribStr = ( String )instanceConfig.( "attrib" );
  18. }
  19.  
  20. }

The custom binding extends the TextBinding class and implements the setup function. The passed component is cast to a FileLocater component and the text component is retrieved from it.

The wizard allows the user to create applications with whatever languages they require. This is done through the languages page on the main wizard path.

The master list component contains a list of all of the languages which are available from the wizard. The selected languages list contains a list of languages which have been selected to date. The master list is populated from a predefined set of languages defined in a datasets file as shown below.

languages.xml

  1. <Datasets>
  2. <dataset id="languagelist">
  3. <lang id="Chinese" res="LST_LANG_ZH" code="zh" requiresutf="yes"
  4. locallang="&aelig;&#177;?&egrave;&#175;"/>
  5. <lang id="English" res="LST_LANG_EN" code="en" include="true"
  6. locallang="English"/>
  7. <lang id="French" res="LST_LANG_FR" code="fr"
  8. locallang="Fran&Atilde;&#167;ais"/>
  9. <lang id="German" res="LST_LANG_DE" code="de"
  10. locallang="Deutsch"/>
  11. </dataset>
  12. <dataset id="wizardlanguages">
  13. <lang id="English" value="English" code="en"/>
  14. <lang id="&aelig;&#177;?&egrave;&#175;" value="&aelig;&#177;?&egrave;&#175;" code="zh" encoding="UTF-8"/>
  15. </dataset>
  16. </Datasets>

This datasets definition show how the model can be used to store much more than just ids and values for a node. In the languageList node each language node has information about the localized language name the language code and whether the language relies on Unicode.

In the LanguagePage class the master list is populated in the code below.

Some of the LanguagePage class

  1. List masterLst, languageLst;
  2. LanguageModel masterListModel, listModel;
  3. Checkbox defaultChk;
  4.  
  5. public void pageCreated()
  6. {
  7. langListMdl = ( BaseModel ) rootModel.get( "languagelist" );
  8.  
  9. languageLst = ( List ) findComponent( "languageLst" );
  10. masterLst = ( List ) findComponent( "masterLst" );
  11. defaultChk = ( Checkbox ) findComponent( "defaultChk" );
  12.  
  13. listModel = new LanguageModel( "selectedLanguages" );
  14. languageLst.setModel( listModel );
  15.  
  16. masterListModel = new LanguageModel();
  17. masterLst.setModel( masterListModel );
  18.  
  19. populateMasterLangs();
  20. populateDefaultLangs();
  21. updateLists();
  22. }
  23.  
  24. private void populateMasterLangs()
  25. {
  26. for ( int i = 0; i < langListMdl.getNumChildren(); i++ ) {
  27. BaseModel langMdl = ( BaseModel ) langListMdl.get( i );
  28. String include = ModelHelper.getAttrib( langMdl, "include" );
  29. if ( ( include == null ) || ( include.compareTo( "true" ) != 0 ) ) {
  30. masterListModel.addElement( langMdl );
  31. }
  32. }
  33. }
  34.  
  35. private void populateDefaultLangs()
  36. {
  37. for ( int i = 0; i < langListMdl.getNumChildren(); i++ ) {
  38. BaseModel langMdl = ( BaseModel ) langListMdl.get( i );
  39. String include = ModelHelper.getAttrib( langMdl, "include" );
  40. if ( ( include != null ) && ( include.compareTo( "true" ) == 0 ) ) {
  41. listModel.addElement( langMdl );
  42. }
  43. }
  44. }
  45.  
  46. private void updateLists()
  47. {
  48. masterLst.updateUI();
  49. languageLst.updateUI();
  50. }

The pageCreated method shows references being obtained to the page components which will be used in the class. New LanguageModel classes are created which are used as models for each of the lists. Taking the masterListModel , the populateMasterLangs function iterates all of the languages in the languagelist dataset and adds those elements whose include attribute is set to false . The converse is done for the listModel within the populateDefaultLangs function. This means that English will appear by default in the selected languages list when the page first appears. The updateLists function needs to be called whenever the list data changes so as to render the changes correctly. The LanguageModel class follows.

The LanguageModel class

  1. public class LanguageModel implements ListModel {
  2.  
  3. BaseModel langMdl;
  4. Project project;
  5.  
  6. public LanguageModel() {
  7. langMdl = new BaseModel();
  8. project = ProjectManager.getCurrentProject();
  9. }
  10.  
  11. public LanguageModel( String path ) {
  12. langMdl = ( BaseModel ) project.getModel().get( path );
  13. }
  14.  
  15. public void setDefault( String name )
  16. {
  17. ModelHelper.setAttrib( langMdl, "default", name );
  18. }
  19.  
  20. public String getDefault()
  21. {
  22. return ModelHelper.getAttrib( langMdl, "default" );
  23. }
  24.  
  25. public void addElement( Object ele )
  26. {
  27. langMdl.append( ( BaseModel ) ele );
  28. }
  29.  
  30. public void removeElement( Object ele )
  31. {
  32. langMdl.remove((BaseModel)langMdl.get(((Integer)ele).intValue()));
  33. }
  34.  
  35. public BaseModel getModelAt( int i )
  36. {
  37. return ( BaseModel )langMdl.get( i );
  38. }
  39.  
  40. public int getSize()
  41. {
  42. return langMdl.getNumChildren();
  43. }
  44.  
  45. public Object getElementAt( int i )
  46. {
  47. BaseModel mdl = ( BaseModel )langMdl.get( i );
  48. String res = ModelHelper.getAttrib( mdl, "res" );
  49. return Translator.translate( res );
  50. }
  51.  
  52. public void addListDataListener( ListDataListener listener ){}
  53. public void removeListDataListener( ListDataListener listener ){}
  54. }

The LanguageModel class implements the ListModel interface and as such needs to implement the required methods such as addElement and removeElement . This class is now effectively acting as a wrapper for the language models and makes it easy to move the model nodes around. This class also takes care of the default language and uses the Translator object to translate the language names to the current application language.

Localization

The application has been completely translated into Simplified Chinese and required some specific techniques to take account of the Unicode characters needed to render the language correctly. The results of that localization can be seen below.

The default language for the wizard is English and that is specified in the startup properties as follows.

The language startup property

  1. ...
  2. Language=en
  3. ...

This means that the text for the language will be stored in a file called en.properies some of which is shown below. For each page in the application which is to be localized automatically by Aria the resource attribute needs to be set in the page XML.

The Page resource attribute

  1. <Page resource="">
  2. ...

Section of the en.properties file

  1. ...
  2. WIZ_PROJ_PROMPTS=Prompts
  3. PROMPT_DESC=Description&#92;:
  4. DATA_LST_NAV_CFG=Configurator navigation
  5. DATA_LST_SCR_DRILL=Simple drilldown
  6. WEL_HOTSPOT_GEN=Create or edit a hotspot file
  7. COL_APP_TYPE=Application type&#92;:
  8. PROJ_BUILD_LOC=Locate the spreadsheet you wish to generate
  9. WEL_CLICK_CONT=Click the next button to continue
  10. ...

This is a typical resource file for a Java application.

The language list on the welcome page is populated from the wizardlanguages dataset declared in the languages.xml file which was shown in the previous section.

Some of the welcome.xml page

  1. ...
  2. <Panel laf="true" name="langPanel" x="0" y="0" w="200" h="30" border="0"
  3. style="header/light" opaque="false">
  4. <Label x="5" y="5" w="80" h="20" style="welcome/prompt"
  5. content="WEL_LANG" opaque="false"/>
  6. <Combo name="langlist" x="85" y="5" w="100" h="20"
  7. style="welcome/data/utf"/>
  8. </Panel>
  9. ...
  10. <Events>
  11. <Event target="langlist" type="FocusHandler" method="changeClicked"/>
  12. <Event target="langlist" type="ActionHandler" method="changeLanguage"/>
  13. ...
  14. <Data>
  15. <Bind target="langlist" source="wizardlanguages" output="wizardlanguages"/>
  16. </Data>
  17. ...

The langlist combobox is bound to the wizardlanguages node of the data model and will appear with a list of its children from which the user can select. There are two events attached to the language list changeClicked and changeLanguage and these are shown below.

Some of the WelcomePage class

  1. ...
  2. ComboBox langlist;
  3. boolean changeClicked = false;
  4.  
  5. public void changeClicked()
  6. {
  7. changeClicked = true;
  8. }
  9.  
  10. public void changeLanguage()
  11. {
  12. if ( changeClicked ) {
  13. String selected = (String) langlist.getSelectedObject();
  14. BaseModel mdl = (BaseModel)rootModel.get( "wizardlanguages/" + selected );
  15. String langCode = ModelHelper.getAttrib( mdl, "code" );
  16. String langEncoding = ModelHelper.getAttrib(mdl, "encoding");
  17. project.setStartupParam("Language", langCode);
  18. Translator.reset( langEncoding == null ? false : true );
  19. pageMgr.reset();
  20. pageMgr.showPage( "welcome", "content" );
  21. pageMgr.showPage( "navPanel", "navPanel" );
  22. pageMgr.showPage( "banner", "banner" );
  23. }
  24. }
  25. ...

The changeClicked boolean variable is set to true only when the user clicks on the languages combobox. This ensures that the code in the changeLanguage function is not executed as the list in populated by the binding. If the variable is true and the user selects a new language then the changeLanguage code is invoked. The selected language is obtained from the list and the corresponding language node is retrieved from the model. The code and encoding attributes are then retrieved from the language node. The Language startup parameter is then set for the application and the Translator is reset to use language encoding or not. Now the page manager is reset and the startup pages are reloaded into their respective target frames.

At this point its worth looking at the Translator object and how it manages the necessary fonts.

The Translator Singleton class

  1. public class Translator {
  2.  
  3. protected static ResourceBundle languageResourceBundle;
  4. private static Translator translator;
  5. private static boolean UTF_Encoding = false;
  6. private static String defaultFont = "NSimSun";
  7.  
  8. private Translator() {
  9. Project proj = ProjectManager.getCurrentProject();
  10. languageResourceBundle =
  11. proj.getResourceBundle( proj.getStartupParam( "Language" ));
  12. }
  13.  
  14. public static Translator getInstance()
  15. {
  16. if ( translator == null )
  17. translator = new Translator();
  18.  
  19. return translator;
  20. }
  21.  
  22. public static String translate( String key )
  23. {
  24. if ( languageResourceBundle == null )
  25. getInstance();
  26. if (( key != null ) && ( languageResourceBundle != null )) {
  27. try {
  28. String trans = languageResourceBundle.getString( key );
  29. if ( trans != null )
  30. return trans;
  31. }
  32. catch ( Exception ex ) {
  33. if ( BuildProperties.DEBUG )
  34. DebugLogger.logWarning( "No translation found for the key: " +
  35. key );
  36. }
  37. }
  38. return key;
  39. }
  40.  
  41. public static void reset()
  42. {
  43. reset( false );
  44. }
  45.  
  46. public static void setUseUTF( boolean use )
  47. {
  48. UTF_Encoding = use;
  49. }
  50.  
  51. public static void reset( boolean useUTF )
  52. {
  53. translator = null;
  54. getInstance();
  55. translator.setUseUTF( useUTF );
  56. UTF_Encoding = useUTF;
  57. }
  58.  
  59. public static boolean useUTF()
  60. {
  61. return UTF_Encoding;
  62. }
  63.  
  64. public static String getDefaultFont()
  65. {
  66. return defaultFont;
  67. }
  68.  
  69. }

The Translator is a singleton class. The call to the reset function with the useUTF parameter informs it whether or not the application is running with a language which requires Unicode encoding. If so the NSimSun font is returned from the getDefaultFont function. This font could just as easily be parameterized. Now each of the StyleFactory in each of the Pages needs to make use of the Translator class in order to construct its components with the correct font so a new StyleManager class is introduced which extends the basic StyleManager class as shown below.

The StyleManager class

  1. public class StyleManager extends org.formaria.style.StyleManager {
  2.  
  3. public Font getFont( String style ) {
  4. Font f = super.getFont( style );
  5. if ( Translator.useUTF() ) {
  6. f = new Font( Translator.getDefaultFont(), f.getStyle(), f.getSize() );
  7. }
  8. return f;
  9. }
  10.  
  11. public Font getFont( Style style ) {
  12. Font f = super.getFont( style );
  13. if ( Translator.useUTF() ) {
  14. f = new Font( Translator.getDefaultFont(), f.getStyle(), f.getSize() );
  15. }
  16. return f;
  17. }
  18.  
  19.  
  20. }

The custom StyleManager class overloads the two getFont functions of the base class and checks the Translator object to see if Unicode is being used. If it is then the Translator's default font is used to construct a new font with the same style and size as specified in the styles file. It's now simply a matter of telling the Aria to use this style manager which is done through the startup properties as follows.

The DefaultStyleManager startup property

  1. ...
  2. DefaultStylemanager=org.formaria.quotation.ui.StyleManager
  3. ...

The Synth Look & Feel

The look and feel of the application which can be see from the screenshots in the preceding sections is achieved through use of synth. This look and feel is only available since JDK 1.5

In order to specify the use of the synth look and feel some startup parameters needs to be set as follows.

Synth look and feel startup properties

  1. ...
  2. LAF=org.formaria.laf.SynthLafInstaller
  3. SynthConfigFile=aria.xsynth
  4. SynthResourceLoader=org.formaria.controls.swing.Welcome
  5. ...

In order to use the look and feel for panels a painter attribute needs to be specified as follows. Specifying the synth logo painter for panels

  1. <Panel x="100" y="80" w="390" h="320" border="1"
  2. painter="org.formaria.swing.LogoBackground" style="synthPanelLight">
  3. <Panel x="5" y="5" w="160" h="150" border="1"
  4. painter="org.formaria.swing.LogoBackground" style="synthPanelLight">
  5. <Label x="5" y="5" w="80" h="20" style="base/prompt" content="LANG_MASTER"/>
  6. <List name="masterLst" x="5" y="25" w="150" h="90" style="base/data"/>
  7. <Button name="addBtn" x="5" y="120" w="150" h="25" content="PROMPT_ADD"
  8. style="base/data"/>
  9. </Panel>
  10. <Panel x="170" y="5" w="210" h="150" border="1"
  11. painter="org.formaria.swing.LogoBackground" style="synthPanelLight">
  12. <Label x="5" y="5" w="125" h="20" style="base/prompt" content="LANG_OTHER"/>
  13. <Label x="5" y="35" w="110" h="20" style="base/prompt" content="LANG_CODE"/>
  14. <Edit name="languageCode" x="120" y="35" w="80" h="20" style="base/data"/>
  15. ...

The aria.synth file needs to be available on the classpath and in this case was placed in the resources folder. The aria.synth file specifies component borders and offsets and also refers to images which it uses to render the components. For example it refers to the following two images to render the buttons. The orange version is for mouse over events giving a very interactive application.

Case Study: Product Selection

  1. Product selection

Building CoolSelector with Aria

CoolSelector is a web-based tool for designing cold-room configurations. It was designed and built for Danfoss Refrigeration and Air Conditioning Controls group to allow their consultants, wholesalers and installers to select and configure all the components and controls needed to meet the cold-room requirements of their customers.

Originally, this sophisticated internet application was designed and coded using a mix of Microsoft tools (ASPs, COM, HTML and JavaScript). Later, we rebuilt the application using Formaria's Aria, resulting in a more streamlined tool with faster response times.

Background

Danfoss A/S, the leading supplier of Air Conditioning and Refrigeration components and controls, asked Formaria to develop a configuration tool to allow them to design cold-rooms for their customers. Cold-rooms are typically used by supermarkets and manufacturing/processing plants to ensure that produce is held at a low enough temperature throughout its storage/processing. They needed a system that would allow them to select and configure all of the components and controls needed to build the cold-rooms. The design tool would be used by consultants, wholesalers and installers who would access Danfoss's website to order the cold-room kit on-line.

Danfoss had a clear picture of their client-side requirements, including an interactive graphical user interface to allow quick and simple selection of components.

Danfoss requested that this application be developed as ASPs. We used COM/DLLs objects for the core selection engine to handle the complex mathematical calculations involved in the physical modelling aspect of the system.

The system, known as `CoolSelector' was launched to much acclaim at the IKK show in 2002 and has already spawned a number of imitations.

Part of the inspiration for Aria

In developing CoolSelector, we maintained a clean separation between the underlying calculation model and the user interface. This separation was to allow the system to change and expand during its lifetime and to support a variety of client interfaces including a standalone, off-line version.

CoolSelector and its underlying data model provided much of the inspiration for Aria

Description of the Existing system

The original implementation of CoolSelector uses a mix of Microsoft technologies: the client application is a set of HTML pages with extensive JavaScript support to provide interaction. The front end communicates with a server by means of an XML calling-mechanism, reminiscent of the SOAP protocol.

As a means of reducing the complexity of the JavaScript and supporting the XML data-exchange and calling-convention, a Java Applet (running within IE's JVM) is used on the client-side.

On the server-side, a set of ASPs provide integration with back-end corporate functions, such as user identification, localization and order processing. The ASPs employ a number of COM objects for input validation, database access, file access and functions such as unit conversion (metres to inches etc...).

Core calculations are implemented in C++ and rely on a SQL Server database and several further COM objects for XML access.

In all, the system comprises HTML, JavaScript, XML, ASPs, VB, Java, C++ and SQL and is deployed on a Microsoft IIS server.

The core design works well and has allowed several extensions and changes to the system to be implemented. However, during development and since deployment we identified several areas with room for improvement.

Room for improvement

There are two main areas where improvements could be made to CoolSelector: firstly the coding, and secondly the deployment.

To some extent both of these problems relate to the use of COM objects and the types of interfaces they expose. From a coding perspective, the level of access to XML and databases is too low, resulting in the use of more "plumbing" code than is desirable. From a deployment perspective the use of COM objects means having to stop and start servers and well known DLL-compatibility issues.

To a lesser extent, the design trade-offs gave rise to several problems. Firstly, using an HTML front-end requires extensive JavaScript coding to provide the required degree of interactivity: JavaScript can be difficult to maintain. Secondly, processing and calculations that could have easily been carried out on most desktop computers were instead carried out on the server with an associated time delay.

Initially, the system was intended to support a variety of browsers, but over time support issues and difficulties with subtle differences in the integration of JavaScript and the underlying object model meant that CoolSelector became an IE-only system.

Given all the above, CoolSelector Mark 1 neared the limits of what can be accomplished with an HTML-based front end.

The Aria experience

The Aria version of CoolSelector started as an experiment to see how Aria performed in the development of a rich/complex client application.

Encouraged by initial successes, we began to add new data-access features to CoolSelector using the Aria data model. Aria's data binding facilities immediately meant that a whole layer of data manipulation disappeared: instead of parsing responses and updating controls, Aria allows controls to be bound to data with a simple declaration. For CoolSelector, this meant that we could update all input fields by simply declaring the data bindings. Furthermore this binding removed another layer of custom coding used to coordinate data between pages. These advantages alone provide a persuasive argument for using Aria.

Aria also greatly simplified the construction of the user interface: HTML was not designed for the creation of interfaces like CoolSelector's and to render CoolSelector's complex interface hundreds of lines of HTML in addition to hundreds of lines of JavaScript were required. By comparison, the Aria interface requires only tens of lines of code. From a maintenance point of view, the importance of this code-length reduction cannot be underestimated: less code means less maintenance!

Next, we decided to try out some core calculations in the Aria version, and the results were astounding. Again the Aria approach paid dividends as we were able to remove large amounts of plumbing code (for XML, Database access and marshalling of server requests and responses). However, the performance impact was the biggest surprise!

We had expected that using Java in place of C++ for the intensive calculation operations needed would result in a sluggish system, but in fact, we found that un-optimized selections happened almost instantaneously instead of in the 20-30 seconds we had grown accustomed to!

Comparisons

To an extent, the results achieved are implicit in using a rich client as opposed to a thin (HTML) client. After all, the rich client provides locality of computing, one of the cornerstones of computing performance.

Perhaps the biggest win comes from the simplification of the architecture and code. Aria takes care of most of the plumbing so the application-specific code need only include the explicit business logic required. Isolating this business logic makes for easy coding and maintenance.

The Aria system comprises Java, XML and some SQL, all in all a lot simpler than the client architecture. Gone are the COM objects, simplifying the server deployment dramatically.

The gains do not stop there! Implicit in the Aria architecture is the ability to operate in an occasionally-connected environment, and therefore an off-line/standalone version is a given rather than an additional requirement. Automatic update and synchronization of an off-line version is also implicit. Therefore, a Aria CoolSelector user will see little difference whether they are on- or off-line.

From a corporate standpoint, Aria CoolSelector can still run within IE's JVM. This means that it can appear just as much a part of the corporate website as any other pages. Rebranding can be achieved by editing a single configuration file and a whole gamut of extensibility and customization features are at hand. It is easy to see how Aria as an inherently open architecture could form the basis of an industry platform.

Results

Direct comparison between the Aria and HTML versions of CoolSelector may be inappropriate as some of the code has been replaced by library code and some gains are implicit in creating a rich client application instead of an HTML client. That said, the Aria version of the application represents a 75% reduction in code and approximately a 50% saving in the number of subsystems used.

Selections with Aria occur in 1-2 seconds, compared to 20-30 seconds with HTML/COM on the same machine - a staggering 3000% improvement in performance! In a multi-tier/multi-node server environment, this would translate to almost a complete removal of the work load. From an application-development point of view, the performance gain is significant in that it opens up the possibility of adding features such as simulation and diagnostic capabilities. Significantly, the Aria version of CoolSelector implicitly includes an updatable, standalone client-application as one of the many other functionality gains. Not only does this make CoolSelector accessible to a wider audience but it also makes it feasible to use CoolSelector on a broader range of devices (WebPads/Tablets/PDAs) some of which may be used by field workers such as service technicians.

Features

CoolSelector has many advanced features. First of all it simplifies the task of choosing a coldroom system for a specific market niche. In the screenshot below you can see the main configuration options on the left hand side. A refrigeration engineer would be more than familiar with these settings and indeed some of these settings such as the voltages would be regional specific.

CoolSelector will remember the user preferences so that once the sytem has been used there is probably no need to set these options very often. Selecting a system then becomes a matter of choosing the appropriate options and entering some operating conditions.

Remember CoolSelector is built using AWT and with just the components built into Aria..

Notice how the settings page contains a language button. This button pops up a dialog where you can choose the language in which the application is displayed. The localization of CoolSelector uses the very same techniques as described in this manual, so you can see that is possible to translate a complete application using the simple methods we described. Here's the same page in German.

CoolSelector featues an interactive way of setting the load. Either the users can enter the data on the top half of the page in a form or they can setup the load interactively by clicking on various parts of the graphic at the bottom. Rules and validations are applied to both.

The beauty of a rich-client application is its ability to take advantage of local computing power and CoolSelector does this to great effect. Load calculations and selections are instantaneous and this is a vast improvement over web based systems. The ease with which the system can be programmed also lends itself to adding more powerful and compelling user interaction.

CoolSelector can work on-line or off-line. In an on-line mode CoolSelector will check for connectivity and provide extra product information from Danfoss's corporate website.

In the screenshot below some information about a component is being displayed.

The Web button provides a link to the Internet usings the JDIC web browser component.

Ultimately the application is an e-commerce application and we get a list of part numbers ready for ordering.

For off-line users and for project mangement purposes CoolSelector also produces detailed summary information that can be saved for quality control purposes.

The summary information can even be exported and printed. The export features include export to office formats so that the selection can be reported as part of a larger document, for example a design specification or as an offer document to a prospective client.

And for the techies in the business there are even diagrams showing the characteristics of the proposed systems operation:

Appendix

Architecture

  1. Architecture

Aria is an application development framework designed to relieve the programmer of many common tasks involved in building smart client applications.

This appendix describes the overall architecture of Aria and details how its subsystems interact. The appendix may be of use to application architects and to application programmers.

The Aria Solution

At its simplest Aria s a set of factories designed to hide some of the complexity involved in application construction. Aria builds upon several well known patterns (see Design Patterns [1]) to simplify object creation and to separate concerns within the application.

Typical of this is the use of factory methods to hide the details of object construction and the use of adapters to provide uniform access to data.

Using these facilities the application developer can offload much of what we term `plumbing' to the Aria library.

The solution offered by Aria and enhanced by Aria is layered in an object oriented fashion so there is considerable control as to how extensively Aria need be used in an application. At its fullest Aria can provide a large proportion of what is needed to build a complete application, apart that is from the application specific business logic

At a high level Aria consists of

 

Figure 1: Overall application structure

Project support for coordination of resources

Most applications deal with a variety of objects and resources and an Aria application is no different. In a bid to coordinate resource usage Aria includes a project concept. This project is little more than a mechanism for holding references to the various resource manager objects and types needed by an application.

The use of a project facilitates access to resources by providing a central access point. Making the project the owner of these resources also means that the responsibility for resource creation and disposal can be coordinated. This coordination helps provide an orderly and predictable application startup sequence.

The term resource is used in a broad context to mean any class or object used by the application and this includes the management objects that form part of the Aria framework. An Aria project therefore has application specific resources such as pages, data, colours and fonts as well as the framework originated resources such as page factories, component/widget factories classes, validation factories and event and data management components.

The project acts as a store for most of an application's data, delegating to other classes to manage things like styles and pages. The project retains ownership of these classes and therefore it in turn owns all the resources used by the application. With this demarcation of resources it is possible to coordinate several project (or modules) within a single JVM and therefore the ProjectManager singleton is used to store references to the projects and it provides access to these projects by name.

Application lifecycle and stubs

The core mechanism for loading the application and the project is encapsulated in the ApplicationContext context class. The class coordinates loading of classes that are used by the project, given that many of these classes can be configured and customized via the startup files and other configuration files. The application context is also responsible for some of the lifecycle interfaces, for instance adding a shutdown hook to coordinate resource cleanup when the JVM exits.

Normally the application context is used in conjunction with an instance of StartupObject . The startup object depends upon the widget set being used and encapsulated some of the widget specific issues (by instantiating an instance of WidgetAdapter specific to the widget set being used), and by managing the display. For instance the basic Applet class for Swing coordinates the setup of the page container used to display Aria's pages. However the startup object relies on the application context and the support classes that it instantiates (like the PageManager ) to manage the application lifecycle.

Several startup objects are provided, one for each of the Widget sets supported by Aria, plus some additional forms that allow customized display such as embedding of Aria applications within other applications.

Factory methods to remove repetition

Perhaps one of themost obvious places to begin our description of Aria is with user interface construction and a description of how widgets are added to an application.

Many Java IDEs rely on the basic Java bean facilities to manipulate user interface (UI) components. These beans provide fine grained access via accessor methods and while this is well suited to generated code it is not very programmer friendly. More often than not once generated the UI code is further manipulated by the programmer and this manipulation may break the IDEs graphical editing capabilities. Not only does this situation rapidly degenerate into ugly code and maintenance issues but it also is cause for excessive code bloat.

Aria provides several factory methods that take car of constructing the UI objects and setting the most common properties such as location and content. Aria also includes support for style sheet like specification of attributes such as colour and typograhical information. These additional attributes can be applied by simply using a different object factory.

The object factories can be used interchangable and also help construct an object hierarachy as components and containers are added.

A single factory method call can replace up to a dozen lines of generated code.

Later this document will detail how this method of object construction can even be removed from the Java code base and declare via XML. This XML declaration however is still routed though the same component factories outlined above.

Aria 3.0 introduced a common mechanism for registering various modules and components. This registration mechanism allows multiple named configuration files to added for most components. The mechanism allows handling multpiple third party components and for qualified selection of components based upon usage or upon a developer defined match/callback

Extensible loaders

Aria extends the page loader mechanism, adding several additional page loaders for different file formats. The loader mechanism is designed to be extensible but in many cases declarative systems can be mapped the GenericBuilder which defines a hinting and mapping format for this purpose. The generic builder can also be further customized, with the addition of extra parsers and interpreters so that systems as diverse as Python, VB and Oracle Forms can be mapped to Aria.

Using this extension mechanism means that providing editor support for these formats is relatively easy as the editor itself employs a customized loader. Once the loader/builder is complete the internal representation of the format should be the standard Aria form (although the widget sets may vary).

Factories to target different devices/platforms

One of the key design goals behind Aria was to target limited devices such as handheld computers. For this reason Aria was initially built to support AWT based UIs and JDKs as early as JDK 1.1. Later support was added for JFC/Swing interfaces and additional widget types.

The relatively low base means that Aria can support a wide range of devices not just the latest desktop computers.

The use of component factories mean that the UI implementation can be changed from AWT to Swing simply by changing a startup parameter. Aria also employs a pluggable architecture for registration of the component factories so there is litle difficulty extending Aria to support other UI widget sets such as the LCDUI found in J2ME devices such as RIM's Blackberry devices of Palm's PDAs.

Ignoring the some the device limitations (such as screen resolution) for a moment this targetable UI support means that the same code can be used to simultaneously support a variety of devices.

Full API access

Unlike many other client solutions Aria provides full access to the underlying API. Widgets and components created with Aria can be accessed and manipulated as though they were constructed directly using the API.

Some wrapper for standard widgets are provided but this is for convenience and to allow switch of implementations. The wrappers are thin layers and do not block access to any of the underlying features.

Furthermore all the normal mechanisms for operating on components such as the built-in event handling are available and can be used in conjuction with the features provided by Aria. If for some reason the Aria approach is found to be limiting then it can be supplemented or replaced by direct coding.

This strategy also means that Aria applications can be extended by adding third party components and tools without restriction. Ultimately the Aria code base is open source and is available under a license that allows modification so (probably as a last resort) any of the Aria features could be modified to meet the applications needs.

Layered approach to styles

As has already been mentioned above Aria support a style sheet like way of configuring the appearance of widgets.

Styles can be declared in an XML style file and applied to the components during the component construction process. All that is needed to employ the style sheets is to use the StyleFactory class to construct the widgets instead of the basic component factory. In fact by default Aria will use the style factory.

Moving style declarations to a separate XML file not only means further cleanup of the UI code but it also facilitates additional features such a rebranding an application. Changing the style file associated with an application way also go some way towards addressing the differences in the device capabilities mentioned above.

Styles can also be applied to an application by using a Java Look and Feel (L&F) or a theme, however the styles sheet support provided by Aria offers a much finer level of control over an application's appearance than could be achieved with a L&F alone.

Modular extension

To support multiple widget sets Aria employs an abstract registration mechanism whereby component factories can be added to an application. The Project class owns the component factory and additional factories can be registered at any time although registration is normally carried out at startup.

The component factories offer a number of interfaces for creation of an object. The build in components are assigned unique identifiers whereas additional factories generally rely on a type name to construct the component.

The component class name can also be used to instantiate a component so that third party components or components not expressly supported by a factory can also be added.

The component factories also make use of the AttribuedComponent interface to set component attributes in a generic way rather than having to have detailed knowledge of the accessor methods and without recourse to reflectionor the bean support classes. This method of setting attributes works well in a cross platform situation where there may be differences in the implementation of a particular feature, for many APIs from JDKs are now deprecated.

As factories are normally registered during startup with the project and as the order in which the factories are registered may be important it is therefore easy to see how important a role the project has in creating a predictable startup.

Page management

The component factories described above can be used on their own without need for other parts of the Aria or Aria frameworks but much advantage would be lost if such an approach is adopted.

Applications rarely consist of a single screen or page so Aria provides additional support for instantiating and uses pages in an application framework. The term `page' derives from the notion of presenting an application with a series of page or screen. Logically these pages may equate to steps within a business process or data capture cycle.

Depending on the type of application pages may be used on a one off basis or reused repeatedly, perhaps with updatable content.

Aria provides a page management facility to store pages and manage the context in which they are displayed. Normally only one page is visible at a time but pages may also be used within a frameset to allow further decomposition of the on screen layout. Each element in a frameset holds an individual page.

The PageManger class is owned by the project and in turn it owns the individual pages in the application. Aria includes page navigation methods such as showPage and showPrevious. If the page manager determines that it does not already hold a reference to the request page it will instantiate the page on behalf of the application.

At its simplest a page is instantiated by invoking the ClassLoader to create a new instance of the class. The page is then inserted into the display area via the PageDisplay interface which is implemented by the applet/application class.

If the page is already loaded and is not the currently displayed page the new pages is swapped into the display and the previous page is removed from the container and hidden.

The page manager invokes several methods during page creation and display to allow the page update its content, these lifecycle methods include notification of page creation, activation, and deactivation.

Since a page may be declared via XML it is not possible to directly instantiate an instance of a page class that will represent the content of the XML file. Instead the page manager delegates to a secondary loader (registered at startup) that processes the XML file and creates and configures the page's components.

During construction an instance of an Page is created or of a derived class and the components are addressed to the new page. Once create in this way the XML originated page is largely indistinguishable from one created from a Java class.

The same lifecycle events are fired and an XML page can therefore use the use the page creation response methods to do any additional setup. In a Java page the constructor might do more than just create the widgets so the page creation method provides an opportunity to do equivalent processing for the XML page.

Given that an XML based page may reuse the same base class definition the page creation response method gains in importance as it may be used to bind references to widgets for subsequent use in the class.

The application context by default instantiates at least one page loader ( PageLoader ) and adds it to the page manager's list of page loaders. The default page loader is the AriaBuilder class that parse the page XML. The page manager may therefore have many different page loaders for various file formats and it will iterate through these page loaders till the requested page is successfully loaded.

XML as a shortcut to declaring content

XML as a means of declaring content opens upon many possibilities. Coupling between business logic and the user interface is reduced and the implementation of the UI is more flexible as it can be rendered for different situations in different ways.

The XML needed to represent a page is also relatively compact in comparison to the byte code produced for an equivalent page. This can be of benefit where bandwidth is an issue.

XML can also be generated on demand and in some situation this might be desirable, for example where repetitive catalogue content is being streamed from a database yet where the content may content subtle variations that need to be accommodated in the layout.

Proxies for event management

As with the UI construction Aria provides extensive support for event handling. Using Aria all the plumbing code needed to direct the application to an event response method is hidden from the programmer. There is no need to declare listeners or adapters, all the application need to is declare its interest in receiving notification of an event.

To handle an event the Aria page container registers a proxy for handling the event and using reflection behind the scenes the appropriate response method is invoked by the proxy. To support this reflection mechanism the response method must be in a class derived from the Page class, normally this is the case.

Figure 3 2: Event dispatch for a mouse event

While the response methods invoked in this way are parameterless access to the event is available through the page. The response method may also be overloaded in a polymorphic fashion and in this way libraries of page types may be constructed. Typical of this is a definition of response methods that handle page navigation functions. Such functions can be provided at a low level and shared by derived classes so that the navigation methods need be defined only once.

Input validation

Using a mechanism similar to the event registration Aria can register validation rules for input fields. The validations are special response methods invoked wherever an input field gains or looses control of the input focus, or upon page transition.

In response to this change of focus a validation rule is invoked. Several predefined validation rules are available, for example a MinMaxValidation defines a rule that checks for that an input in with a specified range of values. The limits or parameters for the validation rule are named and the name is associated with values read from a configuration file (by default validations.xml). The validation parameters can even contain method calls that are evaluated at runtime.

Each Aria page can have a validation factory that will construct the parameterized validations on behalf of the page. The page delegates addition of the validation rule to a validation handle that adds the appropriate event handlers to the component being validated. The validation handler coordinates the validation rule checking in cases where more that one rule needs to be evaluates or where dependencies exist.

Some validations may be marked as mandatory so that their input must be valid before certain actions such as page transitions can take place. The validation handler object provides some several methods for checking and rechecking the state of a page's inputs.

Should an error (such as an out of range value) be encounter while validating an input an exception is thrown. The exception is handled by an instance of the ValidationExceptionHandler class. By default it is the page that actually that implements and handles this response by displaying a warning or error message. However the application can override the processing to provide a page specific way of handling the error or the application may choose to register a completely different error handler. An example of this might be where an application chooses to display a list of error in a common window instead of popping up separate error messages for each error.

Figure 4: Exception handling during validation

Data modelling

An application is of little value without data and very often an application serves to do very little other than providing convenient access to or representations of data.

Much of the logic in a form or page based application can revolve around getting and setting the values of UI components. Much of this work is repetitive and mechanistic.

To remove the burden of managing this data access Aria provides a hierarchical data model. This model can be accessed using an XPath like syntax so that the application programmer need not be concerned with the details of how a model node is reached and accessed apart from specifying the path to the node.

Each node in this model can be a simple primitive type such as a string or number or it can be rich types like a list object or even a database table.

The DataModel class provides an abstract representation of the nodes in the model. The model also acts a way of unifying all the data used within the application although data of different types (database tables for example) may be maintained on a different branch within the overall model.

This single data hierarchy and common access mechanism greatly simplifies data access. Despite this rich capability one size rarely fits all uses and therefore the model architecture is extensible right from the management of the model through to the representatation and usage of the model nodes.

Data loading

Once again the project class owns the model and upon startup the applet instantiates a data source (DataSource in the basic Aria setup). The data source can then load the data from various sources. Aria's data source reads a number of configuration file and has multiple data sources. The data loaded by the data source is attached to the model at a node specified by data source's XML configuration file.

At this point the model need not be fully populated and some data may be loaded on demand. In some cases the model may not actually store the data and instead the model node may merely be an interface through which data is accessed. Database access is an example of this as is access to remote data and services.

At runtime the model may also be extended by the addition of sub branches as more data becomes available.

The creation state of the model is largely transparent to the application. For example if a node is accessed it will be automatically created if not already in existence. In this way storage can be made available for say a calculated value or an input value as needed. Frequently calculated data is assembled or produced only as progress is made through an application session.

Indeed this dynamic extension to the model can greatly facilitate the simplification of an application as it need not know very much about the overall state of the underlying data model. In practice this agnostic approach to the model allows opportunities for `lazy' loading of data and for redundant data sources (i.e. where a failover source of data is available should the primary source be unavailable for any reason).

Data binding

Although Aria provides a simple method of accessing data in the model it is unnecessary to use this method display and store data in the UI components. Data in the model can be simply bound to UI components.

Just as validation rules and event response methods can be associated with UI components as though they were component attributes data can also be bound in this way. Again access is through the page container and all the application need to is specify which piece of data to bind to an individual component.

Beneath the scenes a factory class is again used to construct data bindings (derived from the DataBinding class) and associate them with the component. The data binding knows how to get and set data and persist it to the model, equally it knows how to pull data from the model it and update the UI state. This two way data update means that the binding can update both the model and the UI without further intervention from the programmer.

The data binding and the underlying model can be seen to act as a data channel for passing information from one page to another without directly referencing one page from another. Thus an axample of this might be where a page displays an input for a user name or ID, if the input field for this information is bound to the model any other page that binds a UI component to the same model node will automatically display the same data once that page is displayed.

Not all data is equal and some UI components demand different data. For instance an edit field might require a simple piece of text or a string a drop down list demands a set or list of data and a table component demands tabular data. Because the required data is not necessarily of the same type or structure as is held at a particular model node an adapter class may need to be created to allow the data be accessed and represented in a meaningful way.

Figure 5 Data binding

The core Aria package provides a limited set of adapters as itself provides only a limited set of model nodes, mostly for static data sources. Aria on the other hand provides much richer and varied data sources. Indeed Aria's data model encapsulates remote data and remote services both of which may return unknown data types. Aria therefore defines an extensible factory for the creation of bindings and adapters. The factory uses a double dispatch mechanism whereby both end of the binding are used to determine the type of binding required. A binding may in fact be composed of several layers of adapters depending upon what is demanded by the UI component and on what is offered by the model.

This flexibility in creating bindings and in handling differing data sources removes considerable burden from the programmer. In this way business logic can be coded with a large degree of disregard for how the data is display and how it is stored.

Equally, the use of adapters and dynamic data binding also increase the flexibility with which a UI can be constructed. Some page can effectively be build without detailed knowledge of the data they will represent. In some applications such as catalogues or browsers this flexibility may make it possible to have a page adapt itself to a broad range of content. Traditionally this would have required custom logic and would have introduced extensive coupling between the presentation and the model.

Database access

One of the most common data sources is the database. Aria provides access to databases via the model. Tables can be preconfigured for specific queries (including parameterized queries) or the queries can be constructed dynamically.

Aria also manages the connections on behalf of the application by providing pooling and by hiding details of the connection from the application. Not only does this remove configuration information from the application code but it also make the coding simpler.

To support the widest possible range of scenarios Aria uses JDBC for the underlying database access but limits itself to a fairly low level of features as some of the lightweight and open source databases have limited features. Aria builts upon this access by then adding features such as a caching layer and replication services.

For the most part once a table has been configured the application can use it as though it were any other node in the model. Rows and Fields can be addressed using XPath like expressions and the tables can be bound to UI components via the sort of adapters mentioned above.

For remote client applications databases can be replicated to the client device on demand and cached locally across sessions. Whenever a database resource is accessed the state of this cache is checked and updates are obtained as needed.

The transparent replication of tables from a server to client make it very easy to move functionality from a server to a client in order to create a standalone client.

The database may be used in the reverse scenario where a client application has been operating off-line and has stored information in the local client side database and needs to update the server. The client will again synchronize with the server side database, uploading data once a new session is initiated. Of course in a multiuser environment this sort of update places special design considerations on the database so that the consistency of any shared data can be preserved.

Services and communication

Aria introduces another special type of node to the model, an ServiceProxy node. This node type is a wrapper for a call to some function or service. At its most basic the service proxy node calls a function and as a result returns data. Access the proxy via the model effectively means calling that function. The function can be paramaterized by the nodes attributes and can be implemented in a variety of ways.

In Aria the service proxies are frequently layered to provide additional processing for features such as data checking, encryption, compression and so on. In some architectures this layers objects are termed `interceptors'.

Another common use of the service proxies is to wrap transport protocols such as Http. As you can imagine with something like an http call there are two sides to the call, a client and a server side. Aria provides for different configurations for the client and server sides and therefore the stack (of services) for each side of such a call can be configured as appropriate.

Routing

The layering of service calls is called routing in Aria. For example a call to a remote business operation might be made over a secure http channel. To provide such a route several services would be used; an encryption service, a compression service and an http service on the client side, while on the server side the call would pass through the http server side service, a (de)compression service, and a (un)encryption service before finally being processed by a business object of some sort.

As you can imaging an application may need several of such service stacks to implement all its needs. Aria allows a service proxy to be routed via any named route. The services at either end of the route act as the endpoints. At one end the service represents the point at which the service/route stack is bound into the model and at the other end the final service nomally implements or interfaces to the business process.

Although a route is comprised of several service proxies the stack of services that comprise the route are treated slightly differently than plain service nodes. A route can be reused by multiple service proxies, for example the http transport route described above could be reused for numerous calls within an application.

Aria's support for routes does not stop with transport protocols. Aria also provides routes for fault tolerant communication, store and forward communications (i.e. data is forwarded once a communications channel becomes available), for redundant routing (communications can be attempted over several routes in parallel) and for failover services (an example might want to provide a default locel implementation if the server cannot be contacted).

Resource Loading

Invariably an application makes use of many resources such as graphics, text, data and files of various formats. An application could simple assume the resources it needs are available or are installed but this would make for an inflexible approach. Furthermore depending on the distribution mechanism the resource could well be located on a client machine in the fie system or in an archive or on a server somewhere. During the lifetime of an application the location of the resource may even change.

Aria's resource manager class provides several methods for locating and retrieving a resource. The resource manager can also construct an input stream on behalf of the application and it may also retrieve objects such as graphics on behalf of the application.

In order to locate and/or retrieve a resource the resource manager attempts to find the resource on the classpath and on the file system. The resource manager can also make use of a custom class loader to search additional paths if needed (this strategy is employed by the IDE at design time).

In addition to this direct loading of resources Aria provides support for extract resources from the classpath to a temporary directory for subsequent loading. This may be necessary where it is assumed that a resource is accessed as a file (some databases make this assumption) or for loding of system files such as native libraries.

MVC pattern for separation of concerns

The Model-View-Controller pattern is another de facto industry standard and already it should be apparent that such a constructional pattern is employed by Aria. Support for this pattern is provided by the factory classes that help encapsulate the various UI components and features, by a rich data model and by the event and validation handling facilities that can be used to control the application in conjunction with the business logic.

The MVC patterns help to separate the architectural and modelling concerns in an application. With such a separation the role of each component is clearer and hence more maintainable.

Java Language

This chapter explains some of the key Java Language features that are used in Aria. It is intended to highlight the impact of these features on Aria rather than being a tutorial or programmer's guide for the language itself.

Don't forget you can use Java

It may seem a little obvious but it can be easy to forget: Aria is a Java based system. The objects created by Aria are all Java objects and you can interact with them using the normal Java programming idioms.

The upshot of this is that you are not constrained to use what is in the Aria framework. If something is missing or does not suit your purposes then you can just code it in Java.

For example Aria provides numerous ways of adding components but sometimes you may have special needs and these needs may not suit the Aria programming idioms. Instead of using saying the component registry or the component factories you can just add the component you need in the page's constructor or in the pageCreated method.

Object types

Java is a strongly typed language but to a certain extent the loose coupling and late binding promoted by the Aria and Aria frameworks can hide this facet of the language. However the framework is primarily a Java based system and ultimately the language's type system will come to play.

This hiding of information may in some circumstances limit the amount of compile time checking carried out by the compiler when building a Aria application. For this reason it is recommended that you use the debug version of the Aria libraries when developing an application. These libraries output a lot of useful debug and diagnostic information and this information can help you track down numerous problems, not just type issues.

As Aria is an open source language you can also obtain the source code and include it in your project so that you can gain even more information about the cause of a particular problem.

If for instance you use the model to pass data between components of your application you may at first be unaware of type issues as the data model is to some extents agnostic about data types. However your application's components will probably require a specific type and if this type is not what was stored in the model you may get type conversion exceptions.

As the chain of events that leads to such situations may be many layers deep you will probably want all the debug information you can get. In the release builds the Aria framework will suppress some error messages and so all you would see would be a message saying that there was a failure invoking a method, with the debug version you should be able to pin-point the source of the error.

Also, with a system comprised of many loosely coupled files you can also get the above types of problems if the wrong type of data has been mapped into the application, but of course this sort of problem is not unique to Aria. Again the debug versions of the libraries can be helpful and with Aria you also get configuration and visualization tools that may help prevent such problems arising in the first place.

Returning to the subject of types, the framework provides the IntegerAdapter and DoubleAdapter classes to make it a little easier to work with the model when using integer and double values. The same techniques used in these classes can just as easily be applied to other types. The adapters simplify access to objects of either Integer or Double within the model and make it less likely that you will forget to make a necessary type conversion. The adapters also have the nice fringe benefit of making the code a little easier to read.

Inheritance

Inheritance is one of the cornerstones of object oriented languages such as Java. One aspect of inheritance is information hiding and this is something that you should be aware of when programming a Aria application. The framework does a lot of work on your behalf and sometimes it is easy to forget this.

One of the most common ommissions is the use of the properties of the base class for pages, Page . The Page class contains several references to key objects like the project manager, the style manager and the project. Remembering to use these variables in your code can save a lot of work.

You should also remember that inheritance may be several levels deep, so don't just stop looking for what you need one level down.

Inheritance also works the other way round too and you can extend many of the features in the Aria framework. Many of the features of the framework are designed with this in mind and are intended to allow you plug-in variations of the default structures and mechanisms.

Package Names

The package name in a Java application should correspond to a directory or folder in the operating system. In Aria this directory must also be a child of the project's source directory.

Special consideration should be given to package naming for cross platform applications and it is worth noting that the file and directory naming conventions vary from operating system to operating system.

In particular Windows users should note that unlike most operating systems Windows is not case sensitive. Furthermore Java is case sensitive so when the JVM loads resources such as images and other files the case of the path and file name is important.

When testing within the development environment under a platform such as Windows it is possible that a file can be loaded even if the case is incorrect as the operting system will ignore such details. However once the application is distributed and when loading from a Jar file the JVM will expect the case to match exactly.

When applied to package names this logic means that the directory names must match the segments of the package name exactly. Windows users should also be aware that Windows Explorer can capitalize the folder names so what is displayed by explorer may not match the actual filename exactly.

Here's what the Java Language Specification has to say about packages:

Programs are organized as sets of packages. Each package has its own set of names for types, which helps to prevent name conflicts. A top level type is accessible (§6.6) outside the package that declares it only if the type is declared public.

The naming structure for packages is hierarchical (§7.1). The members of a package are class and interface types (§7.6), which are declared in compilation units of the package, and subpackages, which may contain compilation units and subpackages of their own.

A package can be stored in a file system (§7.2.1) or in a database (§7.2.2). Packages that are stored in a file system have certain constraints on the organization of their compilation units to allow a simple implementation to find classes easily.

A package consists of a number of compilation units (§7.3). A compilation unit automatically has access to all types declared in its package and also automatically imports all of the public types declared in the predefined package java.lang.

For small programs and casual development, a package can be unnamed (§7.4.2) or have a simple name, but if code is to be widely distributed, unique package names should be chosen (§7.7). This can prevent the conflicts that would otherwise occur if two development groups happened to pick the same package name and these packages were later to be used in a single program.

Classloaders

Aria makes extensive use of classloaders both within the editor and at runtime. Aria even includes a custom classloader so that you can access resources from the standard Aria folders (pages, resources, classes and lang) without having to include those folder names in the file path.

The classloader infrastructure comes into play whenever you need to load files, classes or other resources. Ultimately the system classloader is used and loads files from the classpath. The impact of this is most apparent if you wish to load third party classes. The classes may be found at design time within Aria as Aria will load extra Jar files in some cases, like for example when components are registered. However at runtime you may have to explicitly alter your classpath by adding references to such Jar files.

Aria may also help you by packaging the classes you use into a Jar file but you may be well advised to review what is included in the Jars so that you can get a more optimal distribution.

Classloading also affects the way in which you use resources. At runtime, once your files are packaged into the Jar files they will be read-only and the paths to the files will be case-sensitive. On a Windows system where the paths and filenames are normally not case sensitive this can represent a significant testing issue, so be advised that you should try and respect the case of paths and filenames at all times.

Reflection

Reflection plays an important role in Aria. Many of the links between you application code and the framework are made via relfection. It may be worth taking a look at the reflection API to see how in works in practice. Notably Aria event handling and dynamic attributes (See Evaluated attributes and helpers.) are invoked via reflection.

Encodings

Encoding is sometimes an issue for Aria and for Java applications. Aria is affected by encodings when working with localized text (see See Localization.), or indeed for any text file resource that the framework loads.

Encodings also come into play when working with client-server systems. Data transmitted over the network will need to be properly encoded so that the data can be correctly decoded on the server side. This encoding of data is also an issue for simple posting of data and Aria includes a service for URL encoding and decoding data.

Even if data is not being transmitted over a network URLs are commonly used and you should be aware of the requirements for encoding URLs. The services provided by Java and Aria make it relatively easy to solve encoding problems so long as you are aware of the issues.

Again, the interaction with the Windows operating system may be the source of confusion as the operating systems constraints on filenames do not match the specification for URLs and if the two are mixed you may well not notice any problems, that is until the naming conditions are broken. Typically this may occur when selecting a path or file name and passing it as a URL.

Annotations

Aria includes support for a number of annotations that you may find useful, and which may save some coding. Annotation support was introduced with JDK 5, so you must be using that JDK or later for the following annotations to work.

 

Annotation

Usage

@Bind

An annotation for binding components and data declaratively. The component becomes valid just before the pageCreated() method is called. The annotation argument is the source path for the binding

@Bind( "a/b/c" )

protected XButton myBtn;

or

@Bind( "a/b/c", extra="output=aria_state/x/y/z,unique=true" )

protected XButton myBtn;

 

where the extra attribute specifies a comma separated list of attribute name-value pairs

@Event

An annotation for binding components and data declaratively. The component becomes valid just before the pageCreated() method is called. The annotation argument is the source path for the binding

 

@Event( method="myHandler", type="ActionHandler" )

protected XButton myBtn;

 

where the extra attribute specifies a comma separated list of attribute name-value pairs

@Find

Performs the equivalent of the findComponent method, finding a component in the XML of the same type. Using this annotation you can avoid the typecasts that are required when using the findComponent method.

 

@Find

private Button nextButton

 

find the button declared in XML as

 

@Page

An annotation for binding a class to a page file for layout and styling information. It is not intended that such pages will be loaded as part of the normal aria application lifecycle, but instead they can be created directly

 

@Page( "foobar.xml" )

public class MyClass

{

...

}

@Validate

An annotation for finding components and binding to an object declaration. The component becomes valid just before the pageCreated() method is called.

 

@Validate( "CreditCard" )

protected Button myBtn;

 

or

 

@Validate( value="CreditCard", method="validate", when="mouseClicked", extra="type=custom,message=msg_res_key" )

protected Button myBtn;

 

with all values other than the value attribute being optional. The extra attribute is a comma separated list of name-value pairs.

Learning Java

For those of you new to the Java programming language here are some resources that may be useful in learning the Java Programming language. Many more resources can be found by searching the Internet and the many Java related websites..

Java learning resources

Learning the Java Language

http://java.sun.com/docs/books/tutorial/java/

Sun's site for learning the Java language

New to Java Center

http://java.sun.com/learning/new2java/

More resources for those of you who are new to the Java programming language.

Java Coffee Break

http://www.javacoffeebreak.com/

Never programmed in Java before? Not sure where to start? This quick tutorial will get you up to speed. You'll learn how to compile and run simple Java applications

Java from the ground up

http://webdeveloper.com/java/java_programming_grounds_up.html

The Java Ranch

http://www.javaranch.com/

A friendly place for Java greenhorns

Introduction to Programming Using Java

http://math.hws.edu/javanotes/

Introduction to Programming Using Java, the fourth edition of a free, on-line textbook on introductory programming, which uses Java as the language of instruction.

Further Resources

Sun's Javasoft website has extensive information about using an learning Java. A good place to start is the New to Java Center:

http://java.sun.com/learning/new2java/index.html

Reference

This chapter includes a collection of cross reference information.

Component attributes

The basic component factories process a small number of attributes that are common to all built-in (core) components:

Common component attributes
Attribute Usage
x The X or horizontal coordinate of the component when a null, absolute or guide layout is used.
y The Y or vertical coordinate of the component when a null, absolute or guide layout is used.
w The width coordinate of the component when a null, absolute or guide layout is used.
h The height coordinate of the component when a null, absolute or guide layout is used.
style The name of the style to be applied to the component
name The name by which the component will be referenced. All components that have events, data bindings, validations or other references should be named.
type The type of the component. This parameter has been deprecated. Originally it was used when all components were specified with a component xml element.
content The usage depends on the component. For components such as labels and edit fields that display test this attribute sets the component test. For others such as images or meta components then the attribute specifies the filename containing the required data or content.
constraint This is the layout manager constraint for the component and it's value should reflect the appropriate layout manager choices.
key This is the key into the resource bundle file. The value is used to set the text in situations where the text is localized or translated. The key should be contained in the resource bundle pointed to by the page's resource attribute.
opaque* Flags an opaque component
visible Sets the visible / invisible state of the component prior to initial display
enabled Sets the enabled / disabled state of the component prior to initial display

A number of components use the AttributedComponent interface to set special attributes, these are listed below:

Component specific attributes
Component Attribute Usage
Checkbox selected Sets the selected state of the check box, values=true|false
Image content A String value specifying the image name. The file name must be one that can be processed by the project and normally this means that the file is on the project classpath or in one of the subdirectories of the project such as the resources folder.
  imagename A synonym for the above.
Label align Sets the horizontal alignment of the label text. Possible values are: "left", "right" and "center"
  alignment A synonym for the above.
  buffered A boolean value to turn double buffering on or off. Double buffering may have a slight impact on the repaint performance, the setting can also be modifed via the toolkit.
  opaque* If true the component paints every pixel within its bounds. Otherwise, the component may not paint some or all of its pixels, allowing the underlying pixels to show through
Panel border Turns on or off the painting of the panel's border. If a value of 0 os used then no frame is drawn. For a value of 1 a single line is drawn around the frame in a colour just darker than the background. If a value of 2 is passed in then a 3d effect is used to show a raised border.
RadioButton selected Sets the selected state of the radio button, values=true|false. Setting this value will automatically set up a group for the radio button if none is already available. A new group is created for the first such radio button encountered within a container or panel.
  alignment* Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default),
SwingConstants.LEFT,
SwingConstants.CENTER,
SwingConstants.LEADING,
SwingConstants.TRAILING
Table headingStyle Sets the header style, the style refers to the styles listed in the Aria style file.
  selectionStyle Sets the style for the selected row within the table
  borderStyle Set the style for the table's border
  interactive Flags whether or not the user can select a row within the table, values=true|false
  updateModel Tie the model selection to the table's selection
Button alignment Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default),
SwingConstants.LEFT,
SwingConstants.CENTER,
SwingConstants.LEADING,
SwingConstants.TRAILING
Edit alignment* Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default),
SwingConstants.LEFT,
SwingConstants.CENTER,
SwingConstants.LEADING,
SwingConstants.TRAILING
  border* Sets the border of this component. A value of zero turns off the border
  editable Sets the editable property of a Swing edit component
  margin* Sets margin space between the text component's border and its text. The value is the size of the margin in pixels.
  format* Sets the formatter for the edit field. The value can be

integer for an edit that only allows integer values

currency for an edit that only allows monetary values

date for an edit that only allows date values

decimal for an edit that only allows decimal values

or a mask specification for a mask format. See the JFormattedTextField documentation for more information. More precise control can be obtained by setting up a formatter in code and using the setFormatter method.

Password alignment* Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default),
SwingConstants.LEFT,
SwingConstants.CENTER,
SwingConstants.LEADING,
SwingConstants.TRAILING
ScrollableMetaContent horizontal scrollbar* Determines when the horizontal scrollbar appears in the scrollpane. Legal values are:"as needed", "always" and "never"
  vertical scrollbar* Determines when the vertical scrollbar appears in the scrollpane. Legal values are:"as needed", "always" and "never"
Scrollbar horizontal scrollbar* Determines when the horizontal scrollbar appears in the scrollpane. Legal values are:"as needed", "always" and "never"
  vertical scrollbar* Determines when the vertical scrollbar appears in the scrollpane. Legal values are:"as needed", "always" and "never"
SplitPane orientation Sets the orientation of the splitter. A value of "horz" creates a horizontal split, anything else gives a vertical split
  location Sets the devider size. The size may be an integer or double value.
  size Set the devider size in pixels.
Table2 headingStyle Sets the header style, the style refers to the styles listed in the Aria style file.
  selectionStyle Sets the style for the selected row within the table
  borderStyle Set the style for the table's border
  interactive Flags whether or not the user can select a row within the table, values=true|false
  updateModel Tie the model selection to the table's selection
TextArea rows* The number of rows
  columns* The number of columns
  wrap* Turn wrapping on or off with a value of true or false
  wordwrap* Turns wrapping at word breaks on or off with a value of true or false
  editable Sets the editable property of a Swing edit component
  border* Creates a line border if this attribute is found
Page class The name of the page class to be used for this page's implementation
  resource The name of the resource bundle
  layout The type of layout manger

*applies to the Swing version of the component only

Startup Properties

The startup.properties file is a key file in controlling the behaviour of an application. This appendix documents the parameters in the startup file.

Properties

The list below contains the basic set of properties used by Aria. The properties file is a generic mechanism and can be used for other purposes such as application specific variables and therefore it cannot be guaranteed that this is an exhaustive list of values that can be found in the startup file.

Startup properties
Property Role
UseWindow (true|false) Toggles use of a separate Window for the application. With a Window the application does not appear in the normal frame of the applet viewer and can appear as though it occupies the fullscreen (depending on the values of ClientWidth and ClientHeight).
ClientWidth Width of the applet or application window in pixels, defaults to 640
ClientHeight Height of the applet or application window in pixels, defaults to 480
StartPackage The name of the package in which the page implementations are found e.g. com.mycompany.mypackage
StartClass The name of the start page or the first page that is displayed. e.g. Welcome . The page name is qualified by prefixing it with the StartPackage name. If a value of NONEis given then no page is loaded. This can be useful when using framesets as the frameset may also specifies content for each of its areas and this could lead to unnecessary reloading of pages in the content area.
Title The application title, shown on the main window.
StyleFile The name of the file containing the style definitions, by default this is styles.xml
ModelData The name of the file listing the datasets used by the application, by default this is datasets.xml
CenterWin A boolean value (true|false) indicating whether or not the application window is to be centered on screen.
DataSourceClass The name of the class that implements the datasource, by default this is org.formaria.data.OptionalDataSource
LibVisualise A boolean indicating whether or not to show the visualizer. The visualizer allows you to see the data in the model at runtime.
Validations The name of the file containing the validation rules, by default this is validations.xml
ValidationFactory The name of an alternative validation factory class.
LogLevel An integer value indicating the logging level used by the debug version of the application.
LifeCycleListener The name of a class that implements the LifeCycleListener interface. This interface is used to perform special shutdown operations.
ComponentRegistry The name of the XML file containing component registry information.
NumComponentFactories Specify the extra component factories to use and the source of registry info
ComponentFactory0 An entry like this is used for each component factory and names the class that implements the component factory.
BuilderClass The name of the default builder class, normall org.formaria.builder.AriaBuilder
DefaultModelClass Sets the default model class. Normally this is BaseModel . The model class has a major role in Aria applications and any changes to this parameter should be undertaken with caution.
SavePath The output path for temporary or cached data. If this setting is not preset or if it is blank Aria will save data to the System specified temporary directory.
TriggerValidations When set to true a page's validations are triggered prior to page transition and a failure in the validations will block the page transition.

And some properties introduced with Aria 2.0:

Startup parameters introduced in version 2.0 of Aria
Property Role
LAF The class name of a Look and Feel installer. A look and feel installer includes a static method installLAF that carries out whatever steps are needed to install the look and feel. The method is called when the application first starts.
SynthConfigFile The file used by the Synth look and feel for its configuration.
SynthResourceLoader The class used by the Synth look and feel to load the resources referenced in the Synth configuration file.
AntiAlias A value of true turns anti-aliasing on for all components within a Swing application.
LogWriter Specify the class name of a log writer, for example org.formaria.log.QueueLogWriterwhich writes to the output stream in a background thread - this provides for better application performance during heavy logging.
ProjectPath The root path of the project. This property is generated dynamically and does not appear in the properties file and is instead set during startup.

And some properties introduced with Aria 3.0:

Startup parameters introduced in version 2.0 of Aria
Property Role
BuilderClass[N] The name of a builder class, where N is the number of the builder such that N is less than NumBuilderClasses2. An attempt is still made to load the default builder class via the bare BuilderClass property.
NumBuilderClasses The number of builder classes to attempt to load.
DataBindingContextClass Specifies a binding context class, an instance of which will be used to manage the PageHelper to manage the bindings. The class should implement the DataBindingContext interface. An example of this is the HibernateDataBindingContext which transparently starts the database transaction (hibernate session) before the bindings are evaluated.
DefaultTarget If this value is set to NONE then the internal frames/desktop style of application will not attempt to set and load a default target area and this means that a desktop/internal frames application can be started without an initial document being required/displayed, i.e. an empty desktop provided that the frames file does not define frames or content areas.

1. Swing only

Embedding

Aria can be used to build applications for embedded devices and PersonalJava devices. Many differences exist between the lastest desktop Java VM and the VMs available for non desktop devices.

Choosing the JDK

The choice of JDK will depend on the various features available and how that feature set matches your requirements. For example on Windows CE platforms there are a number of JVMs available including Esmertec's Jeode and NSI's CreMe VMs. Licensing costs also vary between VMs and can be an important consideration.

While most VMs will at least support JDK 1.1.x there may even be differences between VMs at this level in terms of how the various libraries are implemented. An example of such differences can be found in the way various AWT widgets such as combo boxes are rendered on different VMs.

Some VMs support a more recent subset of the latest JDK but you will need to test such features to ensure that suitable behavior is obtained. It is not enough to test on the desktop and just deploy to an embedded device, but you probably knew that already.

Choosing the right libraries

In the underlying Aria platform some bits of code are excluded as they are not supported on early JVMs. For this reason a special build for the JDK 1.1.x compatible JVMs has been released (this also supports Internet Explorer's built-in JVM).

Rebuilding the libraries

If the JVM you are using does not match either the desktop or the JDK 1.1.x built then you will need to rebuild the Aria libraries.

Removing the shutdown hooks

The desktop build of Applet includes a shutdown hook for purposes of logging some summary information to the console when the JCM shuts down. To build for early JVMs it may be necessary to exclude the classes in the org.formaria.aria.build.optional package from the build.

Real-time support

We are currently working on adding support for real-time Java (via RTJS). For further information please contact Formaria directly.

Building

  1. Building Aria

The complete source for both Aria and AriaEditor is available from the SourceForge repository. The files are available in the Subversion repository and a few simple steps are needed to check out and build the projects.

Accessing SVN

The Subversion repository can be access anonymously either via the command line or via a tool such as SmartSVN or TortoiseSVN.

Subversion connection parameters

Parameter

Value

Server

https://svn.sourceforge.net

Path

/svnroot/aria/

The projects require a number of support project, all of which you can check out from the repository to C:\SVN or the equivalent for Unix/Linux platforms. These projects are:

Projects in the SVN repository

Project

Role

Aria

The main Aria project

AriaALL

Builds the Aria NBM, NetBeans module

AriaSuite

Ties the various NBMs together.

AriaEditor

The AriaEditor editor NBM

lib

A collection of third party libraries

javac

JDK files

The javac project is not completely contained in the repository due to licensing issues.

Preparing to build

Before building a couple of preparation tasks need to be performed:

1. Copy the contents of the JDKs to the javac project. The JDK 1.1.8, 1.3 and 1.5 files will be needed. At least the bin , lib and jre folders are required. The files are required to build the various versions of the Aria libraries

2. Update NetBeans (via the Tools|Update Center menu option) following the initial installation to ensure that the latest NetBenas module development support is available.

3. Open the Aria , AriaSuite and AriaEditor projects. In opening these projects other referenced projects may be opened, but you can close these.

4. Modify the common.xml file under the NetBeans/harness folder (under Program Files on Windows) so that:

is replaced with:

5. Check that the AriaEditor/src/nbproject/private/private.properties contains a value for module.excludes like:

module.excludes=**/net/Formaria/editor/eclipse/**/*

Building

Building the project should now be straightforward.

1. First build the Aria project by right clicking in the project view and choose Clean and Build All .

2. Now open the Aria module under the AriaSuite project. When the project opens (it should have a different icon to the main Aria project) build it, this creates the .nbm file for Aria project. This file is referenced by the AriaEditor module.

3. Build the AriaEditor module.

And that's it, you should be able to launch a new instance of NetBeans from within the IDE to run or debug the module.

Eclipse

The AriaEditor is available for Eclipse from the SourceForge repository. The Eclipse version shows some differences from the NetBeans plugin and this appendix is intended to guide you through some of the differences.

Installing the plugin

Creating a new project

The process of creating a new Eclipse project is very similar to that described in A quick tour of the editor.html.

Accessing SVN

The Subversion repository can be access anonymously either via the command line or via a tool such as SmartSVN or TortoiseSVN.

Subversion connection parameters

Parameter

Value

Server

https://svn.sourceforge.net

Path

/svnroot/aria/

The projects require a number of support project, all of which you can check out from the repository to C:\SVN or the equivalent for Unix/Linux platforms. These projects are:

Projects in the SVN repository

Project

Role

Aria

The main Aria project

AriaALL

Builds the Aria NBM, NetBeans module

AriaSuite

Ties the various NBMs together.

AriaEditor

The AriaEditor editor NBM

lib

A collection of third party libraries

javac

JDK files

The javac project is not completely contained in the repository due to licensing issues.

Preparing to build

Before building a couple of preparation tasks need to be performed:

1. Copy the contents of the JDKs to the javac project. The JDK 1.1.8, 1.3 and 1.5 files will be needed. At least the bin , lib and jre folders are required. The files are required to build the various versions of the Aria libraries

2. Update NetBeans (via the Tools|Update Center menu option) following the initial installation to ensure that the latest NetBenas module development support is available.

3. Open the Aria , AriaSuite and AriaEditor projects. In opening these projects other referenced projects may be opened, but you can close these.

4. Modify the common.xml file under the NetBeans/harness folder (under Program Files on Windows) so that:

is replaced with:

5. Check that the AriaEditor/src/nbproject/private/private.properties contains a value for module.excludes like:

module.excludes=**/net/Formaria/editor/eclipse/**/*

Building

Building the project should now be straightforward.

1. First build the Aria project by right clicking in the project view and choose Clean and Build All .

2. Now open the Aria module under the AriaSuite project. When the project opens (it should have a different icon to the main Aria project) build it, this creates the .nbm file for Aria project. This file is referenced by the AriaEditor module.

3. Build the AriaEditor module.

And that's it, you should be able to launch a new instance of NetBeans from within the IDE to run or debug the module.

Widgets

  1. Widgets Sets

Aria and Aria support a number of widget sets, some sharing a common ancestory and others significantly different. Aria abstracts the widget concept so that it can work with various widget sets in a consistent way. The ability to handle multiple widget sets means that with certain limitations you can switch between widgets sets without having to change large parts of your application.

TODO - Lots

Widgets sets

Swing

Swing is probably the most sophisticated and best supported widget set supported by Aria. Most of the advanced graphcal components supported by Aria are Swing components and rely on the Java2D library for much of their power.

Swing is available on a wide variety of platforms, inlcuding some mobile devices. Swing's power really shines through on the desktop where it can be combined with technologies like Aria and Java Webstart to deliver compelling applications.

AWT

AWT, the original Java widget set lacks the sophistication of Swing but still allows powerful applications to be constructed. AWT has fewer components available than Swing, and this is reflected in Aria's support. The AWT and Swing widget support in Aria has been designed to allow easy switching rather than strict adherance to either API.

SWT

A major revision and implementation of the SWT widget set has taken place. The most important widgets are now wrapped and usable in Aria. The applet/application has also been updated to share the very latest Aria infrastructure. A number of helpers, utilities and layout components have also been included. Along with the core SWT development a sample SWT application manager has been included, the Aria International Soccer Manager, demonstrates how to build a complete application using Aria and SWT.

HTML

While Aria includes some HTML support it is incomplete and at the time of writing it should be considered experimental. The HTML support is designed to allow features of an application to be delivered via a web browser or to allow integration of Ajax like components.

J#

J# is a Microsoft .Net platform modelled on Java and the AWT. J# also includes some incomplete support for Swing like components. J# support may be of interest to those developers who must support devices where no JVM is available. Aria's J# support is functional, but should still be regarded as experimental.

Others

Some third parties have used Aria with other widgets sets on mobile devices (e.g. the LCDUI), please see the Aria forums for more information.

Widget abstraction

Portable coding

The ability to switch widgets sets is probably of most interest to developers of mobile applications who may have to support embedded applications. While Swing is becoming more widespread many mobile devices still do not support it and therefore AWT or SWT may be a more viable options.

Design considerations

Of course any common abstraction can make it difficult to support widget specific features and Aria's widget abstraction is no different. That said Aria does little to hinder access to native features, so you can still build the best possible UI.

The key to writing portable applications is to isolate the widget specific code as much as possible.

Modularity

Finding a JVM

Upgrading

  1. Upgrading

A significant number of changes have occured between version 1.0.4 of Aria and Aria 2.0. Many of these changes are barely noticeable but other include complete refactoring of components and subsystems and the deprecation of whole classes. This chapter covers some of the major issues involved in upgrading from previous versions of the Aria library.

Projects

In earlier versions of Aria a number of singletons were used to provide easy access to key classes and key resources. While this worked reasonably well for a single project it meant that multiple projects did not sit well with one another in the same JVM. The use of static references also complicated the initialization process and therefore the process of loading project resources.

Aria 2.0 does away with almost all of these statics and instead has one central reference point, the ProjectManager . The project manager class in effect now `owns' each project and is also the root of the object hierarchy. Each project in turn owns a set of manager classes (the classes that were singletons). Many of the reference methods to the singleton classes are still provided but they merely delegate to the ProjectManager and Project classes.

Many methods have now been expanded to include a reference to the owner project and in this way it is a little easier to ensure the correct object hierarchy. Many of the classes that use project references in this way include accessible member variables that store the project reference, for example the Page class now contains a protected member project .

Projects

As part of the restructuring of projects the ResourceManager has been deprecated and moved. The class was no longer consistent with the role of the project as the owner of resources. In most cases a reference to the ResourceManager can be replaced with a reference to the project and in the case of pages this reference is available in the parent class as mentioned above.

Page split

Another major change introduced in Aria 2.0 is the splitting of the Page class. The Page class is probably the most important class in the entire framework and contains many valuable methods. The class was part of the user interface component hierarchy and was therefore shared across widget packages. As the library has grown it has become more difficult to maintain this hierarchy without duplication and therefore we split the page class. Now most of the functionality of the class is provided by the PageHelper class.

Most of the time you should be unaware of the change. In some cases you may want to refer to the member variables in the class and for this reason access to the members has been deliberately left open.

The split also facilitates alternative page implementations, perhaps even for other platforms.

ComponentAdapters

The AWT and Swing widgets in Aria 1.0.4 all shared a common ancestor, the java.awt.Component class. Unfortunately this does not hold true for other widget sets include SWT and most other widget sets. To provide support for other widget sets we have abstracted the interaction between the framework and the widgets using a ComponentAdapter with concrete implementations of this interface being provided for Swing, AWT and SWT widget sets.

Widening of methods

One consequence of the broader support for widget sets is that we needed to widen some of the interfaces and hence many methods now take Object references where previously the took Component or Container references.

Some methods such as findComponent also now return Object references and you may need to cast the return value to the type you need.

Page references

The page class now has a number of member variables for convenience and to facilitate coding. These include references to the project and the model root. Please refer to the API document for more details.

New features

Some parts of the code and notably the examples have been changed to use new features introduced in Aria 2.0. Examples of this include the NavigationHelper class that now employs library functions whereas previously in necessitated a class derived from Page .

Aria 3.0 deprecates the NavigationHelper class as it no longer servers much use.

Deprecation

Several classes are now deprecated, these classes are moved to the org.formaria.deprecated package. The classes remain functional but be advised that we will probably drop the classes from the next major distribution of Aria.

Omitted components

Aria now relies of NetBeans to provide many of the editing facilities needed to build projects (of course you can still build applications using other tools). The NetBeans plug--in, Aria Editor is now a powerful tool with which to build Aria and Aria applications.

because of this move to the NetBeans platform the editor that was part of early distributions is now entirely omitted and will not be maintained. The new editor has a much richer feature set.

What's new in version 3.0

The Juice project (the codename for Aria and Aria 3.0) made a very large number of additions to the two platforms and the associated IDE plugins. This chapter presents some of the new features and details of the changes you will need to make to upgrade your application.

New features

All of the features covered here are also covered in greater depth throughout this manual. The following is a compilation of these features designed to give you an overview of what is new and enhanced.

New Aria features

The features presented below are listed in the order in which they were added to Aria.

Added support for multiple page loaders

Previously only a single page loader could be used and normally this was the AriaBuilder class. By adding support for multiple loaders we can support multiple file formats, including html via additional builders. The individual builders can check the file types and attempt to load the requested pages. This change involves a number of modifications to the interface including renaming the PageManager.setSecondaryLoader method to PageManager.addSecondaryLoader and adding a return value to the PageLoader.loadFrames method

Modified the build and the ContentHolder interface

The build and the source for the full set of JDKs/compiles has been modified. Previously the ContentHolder interface was modified to as the return type checking prevented the getComponent method from returning an Object instead of a Component (which would not work for the SWT/HTML components).

Replaced Flexdoc with a new docking framework

The flexdock framework has been replaced with a new docking framework based upon the MultiSplitLayout from Hans Muller/ SwingLabs. The new framework features drag and drop, zooming, docking etc... and should be considerably more flexible and reliable. The docking framework does not rely on other parts of Aria and can be used independantly.

Added an HTML Builder

The HTML builder makes it possible to load pages from HTML and generate a Swing user interface from those pages. The builder makes uses of a set of prototype tag handlers that instantiate Swing components for the the HTML elements and add them to the page. TableLayout is used for handling tables and this library is added o the libs folder. This new feature also makes use of the new support for multiple page loaders so that it is not necessary to specify whether or not a page is XML or HTML, whichever is found will be loaded. As the AriaBuilder is the initial builder it will get preference and will make the first attempt to load the page. An HTML builder can be installed or configured by startup parameters.

Toolbar support added

A new element in the framsets file has been added to provide support for toolbars. The toolbars will be treated as a special page and docked into the application frame. A new widget, the ToolbarButton has also been added to help implement toolbars. The toolbar buttons can take both text and icons.

New splash screen functionality

New splash screen functionality has been added with the SplashWindow class, based on work by Werner Randelshofer. The new class is set as the main entry point, loads and then invokes the Aria start class proper. This new splash functionality appears alot quicker than the previously documented method of creating a splash screen. The functionality of this splash screen is available across all JDKs and is independent of the new splash screen functionality in Mustang.

Component registration extended

An extended method of registering components has been added. Now by specifying the reflect="true" parameter for a component it is possible to register a component in a single line. Any subsequent access of a component attribute will use reflection to find the appropriate accessor method, and these accessors are then cached for reuse. To register swing components for example all that is needed is the following in the project's components.xml file:

Reflective component property registration

  1. <Component name="JButton" class="javax.swing.JButton" reflect="true"/>

In addition to this extended registration, the editor now adds references to the Jar file used by the components as they are added to a project and therefore editing the component registration is only necessary if you are adding new or third party components, or if you need to do some special customization. In most cases the component writer will take care of registration.

Extended localization support

A set of new classes for loading resource bundles has been added. Of these, the new EncodedLanguageResourceBundleLoader class allows an additional file, a '.propety_encoding' file to be specified for each language. The file can include a value for the associated properties file endcoding and a set of font maps for that language. The font map can be used to replace fonts on a face-by-face basis, or as a combination of face name and point size.

Added support for multiple projects

A set of new classes for loading resource bundles has been added. Of these, the new EncodedLanguageResourceBundleLoader class allows an additional file, a '.propety_encoding' file to be specified for each language. The file can include a value for the associated properties file endcoding and a set of font maps for that language. The font map can be used to replace fonts on a face-by-face basis, or as a combination of face name and point size.

Added input validation feedback

Input fields can now display feedback based upon the success or failure of input validations. So far the validation feedback styles are set on a global basis via the BaseValidator.setValidationColors method.

System colors support added

Support for the system colors has now been added and the named colors can be used in the style files. For example a value can now be specified. See the java.awt.SystemColor class for more details

Specifying a system colour

  1. <color_fore value="activeCaption"/>
Extended style support added

Styles including values other than the basic font and color attributes can now be created. The styles are created as instances of StyleEx instead of the basic Style and these new styles can have any style attribute. When the styles are loaded storage is added for any style name. Once the style has loaded it is marked as closed and further styles can not be (by default) added.

This extension of the style system will make it possible to handle far more component attributes as styles and therefore further consistency can be added to applications. For the built in components the extra style attributes can be used to control values such as alignment, indents, and many other attributes. When the style is being applied to a component following construction the extra attribute values will be set either via the AttributedComponent interface or via reflection, if the particular component does not support that interface.

In the example below, the extended style is indicated by the extended="true" attribute of the style element, the extra property is specified as the alignment element

Extended styles

  1. <style name="label" extended="true">
  2. <color_back value="ffffff"/>
  3. <font_face value="Arial"/>
  4. <font_size value="11"/>
  5. <font_weight value="0"/>
  6. <font_italic value="0"/>
  7. <alignment value="right"/>
  8. <style name="bold">
  9. <font_weight value="1"/>
  10. </style>
  11. </style>
Validations triggered on page transition

Added a TriggerValidations startup properties that is set to true by the new project wizard - and therefore older projects will be unaffected. The flag is read by the PageManager and if true causes the validations to be checked prior to page transition. If the validations fail the page transition will be blocked such that the user must review and correct the reported validation errors.

An exception handler has been added to the attribute evaluator

An exception handler has been added to the attribute evaluator. The handler gets called in case of an exception and can override the result returned by the attribute evaluator. The evaluator may be of use in case, for example, an evaluation depends on a list selection and where that list may not have a selected values - the list would otherwise return a value such as null or -1 to indicate the error and this is probably not a valid value for the evaluated attribute. Say a path of a/b/${c}/d/e is enetered and ${c} depends on say a list selection and that list is not fully initialized.

SWT support enhanced and extended

A major revision and implementation of the SWT widget set has taken place. The most important widgets are now wrapped and usable in Aria. The applet/application has also been updated to share the very latest Aria infrastructure. A number of helpers, utilities and layout components have also been included. Along with the core SWT development a sample SWT application manager has been included, the Aria International Soccer Manager, demonstrates how to build a complete application using Aria and SWT.

The home page is now optional

The home page can now be excluded by specifying a value of NONE if the startup properties. Preventing the home page from loading may be desireable in the case of framesets or other application types where the frames file may also contain a specification for the home page.

Annotation support added

Added annotation support such that the page class can have the following annotations

Annotations

  1. @Page( "foobar" )
  2. class MyClass extends Page
  3. {
  4. @Find
  5. private Button myBtn;</P>
  6. </TD>
  7. </TR>
  8. <TR>
  9. <TD ROWSPAN="1" COLSPAN="1">
  10. <P>
  11. @Bind( "a/b/c" )
  12. private Edit myInput;</P>
  13. </TD>
  14. </TR>
  15. <TR>
  16. <TD ROWSPAN="1" COLSPAN="1">
  17. <P>
  18. @Validate( "CreditCardRule" )
  19. private Edit cardNumber;</P>
  20. </TD>
  21. </TR>
  22. <TR>
  23. <TD ROWSPAN="1" COLSPAN="1">
  24. <P>
  25. @Event( method="doProcessing", type="ActionHandler" )
  26. private Button processBtn;
  27. </P>
  28. </TD>
  29. </TR>
  30. <TR>
  31. <TD ROWSPAN="1" COLSPAN="1">
  32. <P>
  33. ...
  34.  
  35. public void doProcessing()
  36. {
  37. ...
  38. }
  39.  
  40. ...
  41. }
Extended the shutdown hook and application lifecycle listeners

The Shutdown hook has been extended to support multiple lifecycle listeners. The lifecycle listener gives an application the opportunity to perform cleanup and shutdown activities before the JVM exits. Typically an application may wish to close file or database connections or free up resources before exiting. A new listener, the DataBaseLifecycleListener has been added as ahelper for those

applications using a database like HSQLDB in in-memory or standalone mode that requires the database to be shutdown via a SHUTDOWN query.

A LayerLayout has been added

The LayerLayout is intented for use with pages and panels where you want to overlay one set of components with another. All the children of the container with the LayerLayout are given the same size and hence overlay one another. The components are located in the order in which they are created. An application may have to set the opaque property of the panels added to the layers so that the layers appear as expected.

The layering is intended to allow things like background decorations to be added and controlled easily. Layering may also be used to implement features such as overlays and modal/lock-out behavior, say for example overlaying a progress animation during a long running operation.

A ColumnLayout has been added

The column layout is a layout in which you can define columns of components. The components are added in rows, but align to the columns. An example usage is in creating forms where a left hand column may contain labels or captions while the right hand column may contain the input fields. Indentation, spacing and padding may be added to control the layout and define the location of individual compoents.

Repeat syntax support extended

The Repeat syntax has been extended to support bindings and events, this should improve the ability to create templates containing bindings and events.

Component customization support added

A new facility for customizing components has been added such that a collection of method calls can be made to customize a component and set properties on that component. The facility is itself customizable, supporting adapters to control how the properties are applied to the component. The customizations can be applied post creation of the component are just before activation of the page, when the component has been populated. For example in the case of a table the property may apply to the table columns rather than the table itself.

In the example below a table is customized in this way:

A customized component

  1. <ScrollPane>
  2. <Table2 name="currentAED" .... customizer="tableCustomizer"/>
  3. </ScrollPane>

the customizer attribute refers to a customization specified in the customizations.xmlfield. The customization file is as follows:

Specifying customizations

  1. <customizer>
  2. <Adaptors>
  3. <Adaptor name="ColumnAdaptor" class="org.formaria.registry.TableColumnCustomizer"/>
  4. </Adaptors></P>
  5. </TD>
  6. </TR>
  7. <TR>
  8. <TD ROWSPAN="1" COLSPAN="1">
  9. <P>
  10. <PropertySets>
  11. <PropertySet name="tableCustomizer" when="1">
  12. <AutoCreateColumnsFromModel args="false"/>
  13. <GridColor args="white"/>
  14. <ViewportBackground args="white"/>
  15. <Background target="parent" value="white"/>
  16. <HeaderRenderer adaptor="ColumnAdaptor"
  17. scope="*" class="org.formaria.swing.table.TableHeaderRenderer"
  18. args="getTableHeader()"/>
  19. <CellRenderer adaptor="ColumnAdaptor"
  20. scope="0-6"
  21. class="org.formaria.swing.table.AltRowTableCellRenderer">
  22. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  23. <AltUnselectedColors args="0,0,102;252,252,255"/>
  24. </CellRenderer></P>
  25. </TD>
  26. </TR>
  27. <TR>
  28. <TD ROWSPAN="1" COLSPAN="1">
  29. <P>
  30. <CellRenderer
  31. adaptor="ColumnAdaptor"
  32. scope="N" class="org.formaria.swing.table.AltRowTableCellRenderer">
  33. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  34. <AltUnselectedColors args="0,0,102;252,252,255"/>
  35. <WrapsText args="true"/>
  36. </CellRenderer>
  37. <AutoResizeMode args="1"/><!--JTable.AUTO_RESIZE_NEXT_COLUMN -->
  38. </PropertySet>
  39. </PropertySets>
  40. </customizer>

The results of this customization is a table like the following, with row striping and customized header:

Painter replaced with SwingX Painter

The painter classes have been refactored to extend the SwingX Painter class. The SwingX interface was very close to the Aria interface and by using the SwingX version we will have access to a wider range of painters.

POJO support added

Support for using POJOs as a backing for the Aria data model has been added. POJOs can be configured from a variety of sources, including hibernate. Most of the configuration takes place automatically via reflection and little needs to be done other than pointing Aria at the root of the POJO model.

POJOs or Plain Old Java Objects support has been added in Aria 3.0. The new support for using POJOs as a backing for the Aria data model means that POJOs can be bound to user interface components just as easily as static data or database data. The POJO model may be home grown or generated with tools such as JDO or Hibernate. Most of the configuration takes place automatically via reflection and little needs to be done other than pointing Aria at the root of the POJO model.

Here's a sample configuration, showing how some POJO properties are overridden and some finders are refined.

Pojo mappings

  1. <Pojos id="beaumont">
  2. <context class="org.formaria.clinic.ClinicInstance" config="pojoConfig.xml"/>
  3. <overrides>
  4. <pojo id="patient" class="org.formaria.clinic.Patient">
  5. <!--identifier id="id" method="getPatientId"/-->
  6. <collection id="patientId" method="getPatients"/>
  7. </pojo>
  8. <pojo id="clinic" class="iorg.formaria.clinic.ClinicDAO">
  9. <property id="id" getter="getClinicId"/></P>
  10. </TD>
  11. </TR>
  12. <TR>
  13. <TD ROWSPAN="1" COLSPAN="1">
  14. <P>
  15. <finder id="patients" getter="getPatients" class="org.formaria.clinic.Patient">
  16. <key id="name" method="getName()"/>
  17. </finder>
  18. <property id="name" getter="getClinicName" setter="setClinicName"/>
  19. </pojo>
  20. </overrides>
  21. <root id="pojo" class="org.formaria.clinic.ClinicDAO">
  22. <param class="String" value="Clinic"/>
  23. </root>
  24. </Pojos>

The POJOs are then attched via the root object that serves to connect the Aria model and POJO hierarchy. A sample usage is as follows:

Pojo binding

  1. <Page class="org.formaria.swing.Dialog" title="Test POJOs" padding="0">
  2. <Components>
  3. <Label name="clinicName" x="10" y="10" w="200" h="20" style="stripe"/>
  4. </Components>
  5. <Data>
  6. <Bind target="clinicName" source="pojo/patients@idx=[1]/name"/>
  7. </Data>
  8. </Page>

where pojo/patients@idx=[1]/name refers to the second item returned by the getPatients() method of the root object. The root object as specified in the configuration is an instance of the ClinicDAO class. The root instance can be parametrized or alternatively it can be instantiated by the POJO context class.

Data bindings refactored

The data bindings in Aria 3.0 have been significantly refactored to work off a common interface. Not only does this unify and simplify the constuction of bindings, but it also makes it possible to have greater control over the creation of bindings and the provision of diagnostic services. See the DataBindingFactory page for more details.

Similar refactorings have taken place in the event handling and validation support.

Set startup objects

Multple startup objects can now be added simply by adding startup property entries in the form:

Listing the startup objects

  1. StartupObject<N>=Name;className
  2. StartupObject<N>=Name;className;Project

where is a counter starting from 0. In the second example the object is constructed via a constructor that takes an instance of the project as an argument

While this may seem like an insignificant change it opens up the possiblity of having multiple applications running within the same JVM and it means that an application can be built from modular elements, elements that can exist as standalone applications or as integrated modules.

Drag and drop support added

Drag and drop supported has been added for selected Swing components. Support is enabled by adding dragEnabled="true" as an attribute. Additional support can be registered by adding a transferhandlers.xml file to the project. The setup is similar to that of the setup of data bindings.

Unknown attributes now set reflectively

f there is no way of setting the attributes of a component directly, or through the AttributedComponent interface, or through a ComponentAdapter then reflection will be used to set the properties. The methods signatures are

parameter types are set using the JVM type signature syntax, for example

Reflective property

  1. <List name="sourceList" x="10" y="11" w="305" h="251" dragEnabled="[Ztrue]"/>
Map page names

A page name can now be paramaterized or mapped, so that a frames file or startup.properties file can specify a key instead of a page name. This key can then be mapped to a specific page depending on the context or on startup parameters.

Mapped page names

  1. // In startup.properties specifc the Lifecycle object
  2. LifeCycleListener=org.formaria.beaumont.ProjectListener
  3.  
  4.  
  5. // In that object map the "START_PAGE" page name
  6. public class ProjectListener implements LifeCycleListener
  7. {
  8. /**
  9.   * Called when the application/applet has been created and initialized.
  10.   * @param project the owner project
  11.   */
  12. public void initialize( Project p )</P>
  13. </TD>
  14. </TR>
  15. <TR>
  16. <TD ROWSPAN="1" COLSPAN="1">
  17. <P>
  18. {
  19.  
  20. String[] args = (String[])project.getObject( "StartupArgs" );
  21. // Set the start page to the 3rd command-line parameter
  22. p.getPageManager().mapPageName( "START_PAGE", args[ 3 ] );
  23. }
  24. } </P>
  25. </TD>
  26. </TR>
  27. <TR>
  28. <TD ROWSPAN="1" COLSPAN="1">
  29. <P>
  30. // In the frames.xml include the "START_PAGE" key
  31. <Frame name="content" constraint="content" width="500" height="200"
  32. content="START_PAGE" title="Home"
  33. icon="ac0036-16.png" sidebar="west"/>
Minimal http server embedded

A minimal http server is now embedded at org.formaria.http.HttpServer. When customized this server will allow a server appplication to signal a client via a http request. By customizing the server with a response handler the application can then respond with special actions, such as a wake-up or by requesting updates/new data from the server.

SystemTray support added

Support for the system tray or launch area has been added. When a system tray icon is added the application can choose to stay resident in memory after the main window has been closed by setting the "ExitOnClose" startup property to false. The advantage of doing this is that the data model does not need to be reinitialized if the main window is just being redisplayed. This initialization can be significantly quicker than normal startup as all

the classes needed by the JVM are already loaded. If remote data is used the startup gain may be even greater as network access may not be required.

The tray icon provides two menu items, one to open the window and the second to close the window and exit the JVM.

This version of the class uses the SwingLabs version of the tray support and is therefore limited to Swing. If JDK 6 or later is being used then the JVM's tray support could be used for non Swing applications.

The system tray/stay resident option may be particularly valuable when used in conjunction with an embedded webserver, as a remote server could thereby signal the application to wake up and display new data or alerts as when such data becomes available by simply making a request to the client application's server. The same process could also be used to provide a tighter integration between a web page/web application and a Aria application.

To use this support the application must add an instance of the org.formaria.swing.deploy.SystemTrayManager class. A good place to do this is in a LifeCycleListener implementation

Using the system tray

  1. public class ProjectListener implements LifeCycleListener
  2. {
  3. private static SystemTrayManager sysTray;
  4.  
  5. /**
  6.   * Called when the application/applet has been created and initialized.
  7.   * @param project the owner project
  8.   */
  9. public void initialize( Project proj )
  10. {
  11. final Project project = proj;
  12. if ( sysTray == null ) {
  13. sysTray = SystemTrayManager.getInstance( project );
  14.  
  15. // Do remaining initialization and network access
  16. }
  17. }</P>
  18. </TD>
  19. </TR>
  20. <TR>
  21. <TD ROWSPAN="1" COLSPAN="1">
  22. <P>
  23. /**
  24.   * Called when the application/applet has been shutdown and is about to exit
  25.   */
  26. public void shutdown()
  27. {
  28. }
  29. }
AriaProxy added to ease Aria-Swing integration

The proxy can be used in place of an applet class where the Aria fraction of an application needs to be integrated with an existing or legacy Swing application. The proxy extends JPanel and encapsulates the Aria elements of the project. An example application is included in the Aria source tree.

Collection support added for tables

The modelling of tables has been refactored so that more generic implementations can be provided, the first of which is construction of tables from ArrayLists and vectors. The new class CollectionTableModel supports the new TableModel, RowModel and FieldModel setup. For example. Using Java arrays with table bindings

  1. public class MyTable extends Page
  2. {
  3. ...
  4.  
  5. public MyTable()
  6. {
  7. String[] names =
  8. { "ID", "Title", "Author" };
  9.  
  10. String[][] rawData = {
  11. { "0", "Moby Dick", "Herman Melville" },
  12. { "1", "The Hunchback of Notre Dame", "Victor Hugo" },
  13. { "2", "The Idiot", "Fyodor Dostoevsky" },
  14. { "3", "Ulysses", "James Joyce" },
  15. { "4", "Thus Spake Zarathustra", "Friedrich Nietzsche" },
  16. { "5", "Bleak House", "Charles Dickens" },
  17. { "6", "Mansfield Park", "Jane Austen" },
  18. { "7", "Alice's Adventures in Wonderland", "Lewis Carroll" },
  19. { "8", "The Republic", "Plato" },
  20. { "9", "Kidnapped", "Robert Louis Stevenson" },
  21. { "10", "On the Duty of Civil Disobedience", "Henry David Thoreau" },
  22. { "11", "The Jungle Book", "Rudyard Kipling" },
  23. { "12", "The Picture of Dorian Gray", "Oscar Wilde" },
  24. { "13", "The Rime of the Ancient Mariner", "Samuel Taylor Coleridge" },
  25. { "14", "Catcher in the Rye", "J. D. Salinger" },
  26. { "15", "The Glass Bead Game", "Herman Hesse" },
  27. };</P>
  28. </TD>
  29. </TR>
  30. <TR>
  31. <TD ROWSPAN="1" COLSPAN="1">
  32. <P>
  33. ArrayList fieldNames = new ArrayList();
  34. for ( String n : names )
  35. fieldNames.add( n );
  36.  
  37. ArrayList data = new ArrayList();
  38. for ( String[] row : rawData ) {
  39. ArrayList rowList = new ArrayList();
  40. for ( String field : row ) {
  41. rowList.add( field );
  42. }
  43. data.add( rowList );
  44. }
  45.  
  46. CollectionTableModel ctm = new CollectionTableModel( project,
  47. "CollectionTable",
  48. fieldNames,
  49. data );
  50. rootModel.append( ctm );
  51. }
  52. ...
  53. }

combines with the XML to bind the collection to the ' collectTable ' table component.:

Binding to a Java array

  1. <Page class="aria.projects.sqltables.MyTable" layout="border">
  2. <Components>
  3. <Label name="title" content="Table Demo" alignment="center"
  4. constraint="north" style="Heading"/>
  5. <TabPanel constraint="center" >
  6. ...
  7. <ScrollPane title="Collections">
  8. <Table2 name="collectionTable" title="collection"
  9. interactive="true" headingStyle="TableHeading"
  10. style="TableData" selectionStyle="TableSelection"
  11. horizontal_scrollbar="as needed"
  12. updateModel="true"/>
  13. </ScrollPane>
  14. </TabPanel></P>
  15. </TD>
  16. </TR>
  17. <TR>
  18. <TD ROWSPAN="1" COLSPAN="1">
  19. <P>
  20. ...
  21. </Components>
  22. <Events>
  23. ...
  24. </Events>
  25. <Data>
  26. ...
  27. <Bind target="collectionTable" source="CollectionTable" output="tables/ct" />
  28. </Data>
  29. </Page>

New AriaEditor features

Added support for SWT

The NewProject wizard has been modified to include support for SWT widgets. The wizard now provides options for Swing, AWT and SWT, and a new startup property (WidgetSet) is output.

Added a list of superclass methods

Added a list of super class methods and the methods in the current class so that they can be selected for reuse in event handlers. Also added a check for the presence of the superclass method (in case it's not in the immediate superclass) and pop up a warning asking if the method should be overloaded.

In-situ editing added

In-situ editing, or the ability to edit component properties in the location where the component is located has been added. As the mouse is moved over a component a translucent property sheet is displayed next to the component. The property sheet can be used purely for information purposes or to modify the properties of a component. The in-situ editing operates in a number of modes, one with a full property sheet, a second minimal mode, and a third mode with showing selected property values. Clicking on these hints gives access to a single property editor for that property.

Support for evaluated attributes added

Support for evaluated attributes within the AriaEditor editor has been added. Almost all properties may now be set as evaluated attributes. The expression will insert full expressions reference methods of the page or if a new expression is entered the stub of that new method will be inserted into the page class.

Dialog size preview added

When a dialog is opened in the page designer a preview of the packed size is overlaid on the designer. The packed size is equal to the size that will include all the components, plus the padding. The size of the dialog, including the headers and borders is also displayed.

Design time support for includes added

Support for includes has been added to the page designer. Included component can now be edited and manipulated as though they are part of the page, and upon save the included file is also saved. Multiple instances of included file(s) can be used.

Locking of components added

A new popup menu has been added that allows components to be locked so that they are not accidentally moved by the mouse. When a component is locked the cursor no longer chenges as the mouse moves over the grab handles, nor does the component move when it is dragged. A small red X is shown in the centre of locked components.

Support for Includes and Repeats

Support for 'include' and 'repeat elements has been added. When a repeat or include element is selected in the inspector a custom property sheet is shown. For convenience the inspector also allows all children of an included or repeat element to be selected so that the user can see what is 'owned' by the element.

Preferred Page Size dialog added

A preferred page size dialog has been added to configure the page size for editing. The dialog shows the sizes that are defined by the frames setup and also allows selection of customs sizes. When the page is subsequently loaded it is displayed at the specified size.

New Aria features

Standalone Language Editor

The language editor is a standalone version of the translation tool that licensees can distribute to translators without having to distribute the whole NetBeans platform.

PDF Exporter

A new PDF export feature has been added, the export package can now export or print HTML, Excel XLS and PDF.

Enhanced Question Manager

Many new features including new question types, templates and expression types.

GenericBuilder

A generic builder has been added. The builder allows a variety of file formats to be mapped to Aria pages, including data and event bindings. The builder does not require a one to one mapping and an element of one format can be mapped to multiple Aria components, or multiple elements within the source file can operate on a single component.

PojoFormBuilder

Extending the GenericBuilder, the POJO form builder inspects a POJO and builds a form based on the available properties. The form can be customized with the same decoration layers as used by the generic builder and by the same type of mappings.

Pojo Visualization

PojoModel objects visualization has been added to the Data Visualizer.

The generic builder also makes use of Aria's new layered pages so that static content can be added to the new page as a background layer to provide visual queues and eye candy to help improve the appearance of the generated pages.

New Components

Aria 3.0 adds many new components, some of which are listed below. Many other components are also enhanced and improved.

New Components

Component

Description

Calendar

An outlook calendar component allowing you to add and interact with dates and appointments. The calendar includes day, week and month views and allows easy navigation from one date to another

Rollup

An outlook like rollup bar. Each category within the rollup bar is a panel, allowing you complete control of the bar's content.

TimeChooser

A drop down list for choosing the time of day, a length of time or a start/end time.

BreadcrumbBar

Displays a list of links to previous pages in the page navigation history.

Wizard

A dialog class that can display and manage the pages of a Wizard.

CheckCombo

A combo box that shows a list of options that can be toggled using combo boxes

New in 3.1

Aria 3.1 is mostly a maintenance release with numerous tweaks and fixes but the most notable changes are listed below. detailed information is available in SVN and in the SourceForge tracker systems.

AriaEditor 3.1 Changes

Styles reparsed

Styles are reparsed if the styles.xml file if modified outside of the editor. On switching back to the editor the modified file is automatically reloaded (if open) and the user is prompted as to whether or not the styles should be reloaded. Normally the styles file is only parsed on startup.

Color Wheel now supports the MouseWheel

The ColorWheel color chooser now supports the MouseWheel, allowing the hue, saturation and brightness to be adjusted using the mouse wheel. Holding the CTRL key down while doing so allows the colors within the wheel to be adjusted for saturation and brightness. There are some videos of this in action at http://www.Formaria.com/aria/videos

Drag to select

A drag to select gesture has been added to the page designer in the IDE plugins. To select multiple components hold down the CTRL key and Drag. A rectangle is drawn showing the selection area and any component intersecting with the rectangle when the mouse is released is selected. Multiple drags can be combines as the drag selection adds to the current selection, unless the SHIFT key is also held down, in which case the intersecting components are removed from the current selection.

To further assist the component selection some keyboard shortcuts have been added.

  • Ctrl + A selects all the components
  • Ctrl + U clears/unselects all the components

This should be a big productivity gain!

Drag and Drop database form generation

A last minute addition to AriaEditor 3.1 is drag and drop form generation for databases. Within NetBeans select the runtime view, configure a database connection and then drag a table on to a page and a form complete with data bindings and validations will be created. The database table will also be registered as part of the Aria data model as part of this action and in can be viewed using the data visualizer.

Aria 3.1 Changes

PageAnnotationProcessor extended

The annotation processor has been extended so that it searches for annotations of superclasses if the superclass implements the new AnnotatedPage interface. The change is designed to allow creation of common page classes that are then customized within an application. The base class can now use annotations.

An example of this is included in the MetroBank example in the aria/samples folder

MouseWheel support added to the ColorWheel

Mouse wheel support has been added to the ColorWheel. The sliders and the color wheel itself now respond to the mouse wheel.

DatabaseTable constructor widened to take a project reference

The DatabaseTable class constructor was widened so as to be consistent with its other constructors, and the dependant classes were updated accordingly (DatabaseTableModel and CachedDatabaseTable)

Added an isAvailable/setAvailable method to ServiceProxy

This allows the proxy to be included or excluded from a call and to record a common state value other than the return value.

ServiceHelper added

A simple helper class to facilitate access to services

commit and setAutoCommit methods added

These new database methods will make it possible to do batch updates with the database connection and database table models.

Alignment options ignores case

The alignment options for the Edit component are now case insensitive.

Drag and drop support refactored

Drag and drop support has been refactored to make it more extensible and to implement the inspector transfer handlers

DataConnection refactored

Synchronization fixes added and then refactored. This is part of a larger work in refactoring the synchronization support in Aria - more to follow

Tree extended with findNearestPath

This new method searches a tree and attempts to select/highlight the given path. The method is designed to be used after a tree binding has updated the tree. In this scenario the tree nodes may be different from the original tree model nodes and therefore the normal methods for selecting a tree path will fail.

Multiple selection support added for lists

Multiple selection support has been added for list components implementing the ListHolder interface. The ListBinding has also been updated to save list selections for multiple selection lists. If a list allows multiple selections then the output node of the binding will contain an array of Objects corresponding to the selection.

Warning added for unclosed connection

A warning has been added for unclosed connections. The executeQuery returns result set and this needs to be closed before another statement can be used on the same connection. Unlike previous versions the connection will be closed prior to opening a new statement and therefore it is advisable to explicitly close the statement using the matching closeQuery call before making another call. Prior to this change the success of such queries varied with the database being used.

Debug message time logged

The time of a debug message is now logged by default. This behavior can be turned of with DebugLogger.setLogMessageTime( false );

Added an exclusive mode for the docking app

Added an exclusive mode for page display so that a docking app can display an initial page without sidebars so that a logon page can be displayed.

Vertical text alignment options added

Extra alignment options have been added so that text in labels and other components can be aligned vertically.

Support for submenus added

Menus can now have nested submenus. These menus can be created with the usual menu XML.

From the International Soccer Manager example:

Sample Menus

  1. <Page layout="Border">
  2. <MenuBar>
  3. <Menu content="File">
  4. <MenuItem content="New Country..." image="menu/new.png"/>
  5. <MenuItem/>
  6. <MenuItem content="Open Country..." image="menu/open.png"/>
  7. <MenuItem/>
  8. <MenuItem content="Save Coutry" image="menu/save.png"/>
  9. <MenuItem content="Save Coutry As..." image="menu/saveas.png"/>
  10. <MenuItem/>
  11. <MenuItem name="exitMI" content="Exit" image="menu/exit.png"/>
  12. </Menu> </P>
  13. </TD>
  14. </TR>
  15. <TR>
  16. <TD ROWSPAN="1" COLSPAN="1">
  17. <P>
  18. <Menu content="Country">
  19. <MenuItem name="belgiumMI" content="Belgium" image="menu/belgium.png"/>
  20. <MenuItem name="englandMI" content="England" image="menu/england.png"/>
  21. <MenuItem name="franceMI" content="France" image="menu/france.png"/>
  22. <MenuItem name="germanyMI" content="Germany" image="menu/germany.png"/>
  23. <MenuItem name="irelandMI" content="Ireland" image="menu/ireland.png"/>
  24. <MenuItem name="italyMI" content="Italy" image="menu/italy.png"/>
  25. <MenuItem name="netherlandMI" content="Netherland" image="menu/netherland.png"/>
  26. <MenuItem name="portugalMI" content="Portugal" image="menu/portugal.png"/>
  27. <MenuItem name="scotlandMI" content="Scotland" image="menu/scotland.png"/>
  28. <MenuItem name="spainMI" content="Spain" image="menu/spain.png"/>
  29. <MenuItem name="switzerlandMI" content="Switzerland" image="menu/switzerland.png"/>
  30. </Menu>
  31. <Menu content="About">
  32. <MenuItem name="applicationMI" content="The Application" image="menu/application.png"/>
  33. <MenuItem name="authorMI" content="The Author" image="menu/author.png"/>
  34. <Menu content="Help">
  35. <MenuItem name="contentsMI" content="Contents" />
  36. <MenuItem name="faqMI" content="FAQ"/>
  37. </Menu>
  38. </Menu>
  39. </MenuBar>
Painters moved and refactored

The painters now use the SwingX painters interface (slightly different from the original version used by Aria). However, since the SwingX version uses generics it has been necessary to add an extra version of the interface to the API. The extra version simply forwards the call to the existing methods. As part of this cleanup the painter classes in Aria's Swing package have been moved to the org.formaria.swing.painters subpackage.