1 – Carousel, User Guide
CAROUSEL User Guide Version 3.0
© Xoetrope Ltd. 2005, All rights reserved.
Copyright © 2003-2005 Xoetrope Ltd. Java is a trademark of Sun Microsystems. Xoetrope reserves the right to modify Carousel in the future and while every effort has been made to ensure this document correctly depicts the software no warranty is made as to the accuracy of this document.
• • • 2 • • • Carousel, User Guide 1 – Table of Contents
Table of Contents
CAROUSEL 2 Table of Contents 3
SECTION I 17 1 About this book...... 19 User Guide Conventions...... 19 XUI and Carousel...... 20 Contacting Xoetrope developer support ...... 20 2 Introduction ...... 21 What is Carousel? ...... 21 What can be built with Carousel?...... 22 What does Carousel do? ...... 23 How does Carousel work? ...... 24 Licensing ...... 26 3 Why Carousel? ...... 27 Background...... 27 Why do we build applications at all? ...... 28 The Form Metaphor ...... 28 Beyond Forms...... 29 HTML’s limits ...... 29 Scripting ...... 30 Application delivery in a connected world...... 30 Rich Client to the Rescue ...... 31 So Why Carousel?...... 31 The Development challenge ...... 31 4 Related technologies ...... 33 Carousel versus HTML ...... 33 Carousel versus AJAX...... 33 Carousel versus Java...... 34 Carousel versus Servlet and Server Side Technologies...... 34 Carousel versus .NET ...... 35 Carousel versus XUL , XForms, XAML et al ...... 35 Carousel versus Ajax ...... 36 Other issues ...... 36 Legacy application support...... 36 Simple single language support ...... 37 Separation of roles ...... 37 Occasionally connected...... 37 Branding ...... 37 Performance and server load...... 37
• • • • 3 • • 1 – Table of Contents Carousel, User Guide
SECTION II 39 5 Installing Carousel ...... 41 Downloading Carousel...... 41 Carousel components ...... 41 XUI components...... 42 Installing NetBeans...... 43 Loading Carousel...... 44 Installing the samples ...... 46 Installing the Eclipse plug-in ...... 47 6 A quick tour of the editor ...... 49 Creating a new project...... 49 The project view...... 55 Pages and resources...... 55 The page designer ...... 57 Components...... 58 Styles...... 60 Source code...... 60 XML ...... 62 Compilation ...... 62 Testing...... 62 Deployment ...... 62 7 Running applications ...... 63 Command-line startup sequence for applications...... 63 Java Web Start Start-up Sequence ...... 64 HTML/Applet Start-up Sequence...... 64 Generic Initialization...... 65 Startup file stubs ...... 66 Eclipse Debugging...... 66 8 Getting started...... 67 Hello world ...... 67 Hello world redux ...... 68 Beyond HelloWorld ...... 69 Building a simple address form...... 70 Applying style...... 71 Validating user inputs ...... 73 Using data ...... 74 Responding to events...... 76 Changing pages ...... 77 9 Projects...... 81 A project’s role ...... 81 Creating a new project...... 82 Project setup...... 83 General setup ...... 84 Frames setup...... 85 Files Setup ...... 86 Extensions setup ...... 87 Extra setup...... 87 Services provided ...... 88
• • • 4 • • • Carousel, User Guide 1 – Table of Contents
10 Designing pages ...... 89 The role of pages ...... 89 Creating a new page ...... 89 Opening the page ...... 89 Opening the page in a frameset...... 90 Preferred page size...... 91 Adding components...... 91 Selecting and sizing a component ...... 92 Nudging a component ...... 93 Aligning components ...... 93 Setting up guides...... 93 Layout managers and constraints...... 94 Setting properties ...... 95 In-situ editing ...... 95 Quickly setting the content ...... 96 Styling a component ...... 96 Delete, Cut, Copy and Paste ...... 97 Locking components...... 97 Hidden components ...... 98 Hooking up code ...... 98 Inserting predefined code...... 99 Working with data ...... 99 Setting up a database table ...... 99 Setting up POJOs ...... 99 Setting up static data ...... 99 Binding ...... 99 Working with validations ...... 100 And now for the XML...... 100 Working with included pages ...... 101 Saving...... 101 11 Building pages with XML...... 103 Basic form building...... 103 Built-in components...... 105 Common properties ...... 107 Panels ...... 108 ScrollPanes ...... 108 Frames ...... 108 Using imported pages ...... 109 Repeat elements...... 109 Layout managers ...... 110 Validations...... 112 Events ...... 113 Data bindings...... 114 12 Building pages with Java ...... 117 Basic form building...... 117 Using stylesheets...... 118 Event handling ...... 119 Data binding...... 120 Validations...... 121 Annotations ...... 123
• • • • 5 • • 1 – Table of Contents Carousel, User Guide
Buddy helpers ...... 123 Other helpers ...... 124 Logging ...... 124 Compilation and building...... 125 Switching toolkits ...... 125 Beyond the basics...... 126
SECTION III 127 13 Styles ...... 129 Using styles ...... 129 Changing a style ...... 130 Color schemes...... 130 Loading styles...... 132 Using System colors...... 133 Extended Styles...... 134 Applying styles ...... 136 Related issues ...... 136 Layout ...... 136 Flow ...... 137 Naming ...... 137 Data/formatting...... 137 Setting the system look and feel...... 137 Setting the Look and Feel...... 137 Configuring Synth...... 138 SkinBuilder...... 138 Using styles with Synth...... 138 Using SVG and Synth ...... 139 Panel backgrounds ...... 140 Painters...... 141 Style guidelines ...... 142 14 Layout...... 143 Default layout...... 143 Limitations of explicit layout...... 143 Alignment tools ...... 144 Grids and Guides ...... 144 Guides...... 145 Adding guides ...... 146 Snapping to guides ...... 149 Snapping to hints...... 149 Setting up Columns ...... 150 Using guides...... 150 An example guide layout ...... 150 Reusing guides ...... 152 Layout managers...... 152 Component hierarchies ...... 154 Using a layout manager ...... 155 Framesets ...... 155 Editing framesets...... 156 Stopping home page from loading ...... 156
• • • 6 • • • Carousel, User Guide 1 – Table of Contents
Application Styles ...... 157 References ...... 157 15 Data binding ...... 159 Data binding...... 160 A simple static data binding ...... 161 Configuring the data source ...... 162 How the bindings work...... 163 Separating data from state ...... 164 Setting up a binding ...... 165 Update on page display ...... 166 Saving values ...... 166 Updating values...... 166 Source and output nodes ...... 167 Callbacks ...... 167 Dynamic bindings...... 168 Adapters ...... 168 Binding tables ...... 168 Selecting tables ...... 168 Linking components...... 170 Advanced attribute evaluation and libraries...... 172 Evaluated attributes ...... 172 Expression evaluators...... 174 Using evaluated attributes ...... 175 Adding new binding types ...... 175 Adding a configuration file...... 175 The configuration file format ...... 175 The structure of a data binding...... 175 Data binding adapter...... 175 Data binding lifecycle ...... 175 Data binding contexts and containers...... 175 Using the XModelHelper ...... 176 16 Event handling...... 177 Adding EventHandlers interactively...... 177 Basic event handling ...... 180 Built-in event handlers ...... 181 Accessing event objects ...... 181 Under the hood ...... 182 Custom event handlers...... 183 Exception handling ...... 184 Generic event handling...... 185 Extended Event Specification...... 186 17 Navigation and page management ...... 189 Navigation ...... 189 The page manager...... 190 Showing a page...... 190 Targets ...... 191 Page history ...... 191 Resetting the page cache...... 191 Page loaders...... 192 Dataset defined navigation...... 192
• • • • 7 • • 1 – Table of Contents Carousel, User Guide
Mapping page names...... 194 18 Application styles...... 195 Application styles ...... 196 Internal Frames...... 196 Docking Frames ...... 196 Using the docking layout ...... 197 Setting the layout configuration ...... 197 Docking options ...... 198 Embedded applications...... 198 Modular application ...... 198 Relaunching applications...... 199 Embedded webserver ...... 200 JNLP support ...... 200 19 Input validation ...... 203 Adding Basic Validations ...... 203 Adding component validations ...... 205 Validation attribute evaluation...... 205 Handling exceptions...... 206 Validation lifecycles ...... 206 Writing a custom exception handler ...... 206 Writing a custom validation factory and custom validations...... 209 Getting values from the XPage...... 212 Adding validation feedback ...... 214 20 Menus and toolbars ...... 217 Creating a Menu...... 217 Hooking up Events ...... 218 Toolbars ...... 221 21 Dialogs...... 223 Creating a dialog ...... 223 Structure...... 223 Display...... 224 Packing...... 224 Modality...... 225 Other issues...... 225 Setting the dialog location ...... 225 Centering the dialog on screen ...... 226 Resizable dialogs ...... 226 Titles...... 226 Helpers...... 226 Focus...... 226 Page activation ...... 226 Data bindings ...... 227 22 Localization ...... 231 The translation mechanism ...... 231 Choosing the language ...... 231 Setting the resource attribute for a page ...... 232 Changing languages in the application...... 233 Localizing the Language Chooser ...... 234 Scanning a project with the language editor...... 234
• • • 8 • • • Carousel, User Guide 1 – Table of Contents
Distribution of language files ...... 235 Encoding issues...... 235 Locale specific properties...... 235 23 The language editor...... 237 A quick tour...... 237 Elements of the language editor ...... 238 Choosing the languages ...... 239 Translation...... 239 Spell checking...... 240 Setup ...... 240 Database configuration ...... 240 Connecting to a database ...... 241 Starting a file based session...... 242 Choosing languages ...... 243 Configuring the spell checker...... 243 Starting a new project ...... 243 Opening a file...... 244 Adding a language ...... 244 Adding a new String...... 245 Adding a translation ...... 245 Substitutions...... 245 Special substitutions...... 246 Remote translation ...... 246 File formats ...... 246 Database table formats ...... 247 Flat file formats ...... 247 The language list (.lst) file...... 247 The language (.jln) file...... 247 The substitution (.sub) file ...... 248 Property files...... 248 Exporting to Microsoft Excel...... 248 Running the standalone editor ...... 249 Redistributing the language editor ...... 249 24 Data management ...... 251 Types of data...... 251 Resource access ...... 252 The data model...... 252 The DataSources ...... 252 Static data ...... 252 Tables ...... 253 Finding data...... 253 Loading and saving files ...... 255 Other data types...... 257 Databases ...... 257 Addings the database DataSource ...... 257 Configuring tables with the NetBeans...... 258 Binding to database tables...... 259 Drag and Drop data binding...... 260 Accessing database tables ...... 260 Distinct and other clauses ...... 261
• • • • 9 • • 1 – Table of Contents Carousel, User Guide
Filtering and finding rows ...... 262 Prepared statements...... 262 Adding data...... 263 Named connections...... 263 Configuring connections...... 264 Debugging...... 264 Databases ...... 264 Transferring data...... 265 Database synchronization...... 265 POJOs ...... 265 Hibernate ...... 267 25 Visualization ...... 269 Using the Visualizer within the Carousel Editor ...... 269 Debugging with the Visualizer...... 270 26 Evaluated attributes and helpers ...... 273 Evaluated attributes...... 273 Arguments allowed in XML attribute method calls ...... 273 Library expressions ...... 274 Expression evaluators...... 275 Using evaluated attributes...... 275 Escape sequence for path attribute values ...... 276 Handling evaluation exceptions...... 276 An example ...... 276 27 Introduction to routes and services ...... 279 Setting up a routing file...... 281 Setting up the Services file ...... 282 Saving a file using the FileSave route ...... 282 Opening a file using the FileSave route...... 285 Creating a custom ServiceProxy class...... 285 Building routes visually with Carousel ...... 288 Summary ...... 293 28 Advanced routes and services...... 295 Session Management and User Authentication ...... 295 Creating a custom client-server service proxy ...... 298 Using datasets to attach to a database ...... 300 Binding a ResultSet to screen components...... 304 Using KalIDEoscope to edit the routes ...... 306 29 Working with components...... 309 Choosing how to install components ...... 309 Using the registry editor...... 309 Installing a component factory...... 312 Setup the registration factory...... 312 Using reflection ...... 312 Using the new components ...... 313 Anatomy of a ComponentFactory ...... 314 A sample component...... 316 Special components...... 319 Hooking up a web browser...... 320 Adding a splash screen ...... 322
• • • 10 • • • Carousel, User Guide 1 – Table of Contents
Swing splash screen ...... 324 Component customization ...... 325 Using Drag and Drop...... 326 30 Using SVG graphics...... 329 Choosing how to install components ...... 329 Component details...... 329 31 Printing and export ...... 333 Direct printing ...... 333 The printing infrastructure ...... 333 Creating print forms ...... 334 Printing tables ...... 334 Other printout issues...... 335 Outputting to file ...... 335 Printing via Excel ...... 342 Printing and interacting with OpenOffice...... 343 Printing via HTML...... 343 32 Drag and drop...... 345 Enabling drag and drop...... 345 Customizing drag and drop...... 345 Drag and drop and data bindings ...... 347 Drag and drop and components...... 347 Extending drag and drop ...... 347 Registering new flavors ...... 347 Drag enabling new components...... 347 33 Importing HTML ...... 349 Opening HTML within the IDE ...... 350 Using the HtmlBuilder ...... 350 Customizing the HtmlBuilder...... 350 34 Importing forms ...... 353 Opening HTML within the IDE ...... 354 35 Packaging and deployment...... 357 Package your project...... 357 Running a Carousel application from the command line ...... 359 Running a Carousel application using Java Web Start...... 359 Setting up a JWS extension...... 362 Deploying a webstart application via CD ...... 362 Running a Carousel application from a web page...... 363 Creating a setup for your Carousel application ...... 363 36 Compile, build and test ...... 367 Debugging ...... 367 Beyond logging...... 368 Debugging with NetBeans ...... 368 Using JBuilder...... 370 Automating testing ...... 373 37 Troubleshooting ...... 375 I’ve built my application but it doesn’t run ...... 375 Only part of my page is appearing...... 376 The 3d effect on buttons is missing...... 377
• • • • 11 • • 1 – Table of Contents Carousel, User Guide
I get an exception when using Synth...... 377
SECTION IV 379 38 Carousel UI Components...... 381 Graphical Components...... 381 XShape ...... 381 XEasterEgg ...... 384 XReflectedImage...... 384 XCaptionedImage ...... 385 Text Components ...... 386 XFlowedTextComponent ...... 386 XHtmlText ...... 388 XPolygonalTextArea ...... 389 XReflectedText...... 391 XRotatedText ...... 392 Animations ...... 393 XAnimatedText...... 393 XCreditsText ...... 394 XMarqueeText ...... 395 Input Controls...... 395 XBaseTable...... 395 XCheckList...... 396 XDateEdit ...... 397 XKeypad...... 397 XMoneyEdit...... 398 XSpinner...... 398 XSlider...... 398 XProgress...... 399 Data Components...... 399 XGraph...... 399 XTreeTable ...... 407 Multimedia Components ...... 409 XAudio ...... 409 XVideo ...... 409
SECTION IV 411 Mortgage application 413 Introduction...... 413 Introduction tutorial...... 414 Advanced Tutorial ...... 419 Bindings ...... 420 Navigation...... 422 Framesets and component registration ...... 424 Dialogs...... 425 Wizard application 427 Introduction...... 427 Framesets and pages...... 428
• • • 12 • • • Carousel, User Guide 1 – Table of Contents
Navigation ...... 430 Component Registration...... 434 Validations...... 437 Data binding...... 442 Localization ...... 446 The Synth Look & Feel ...... 450 ...... Surveys 453 A typical survey ...... 453 A welcome screen...... 454 A survey wizard ...... 455 Managing the survey data ...... 458 Setting up survey templates...... 459 Embedding a survey...... 459 Setting up a survey database ...... 459 Configuing a survey ...... 459 Using rules...... 459 Product selection 461 Building CoolSelector with XUI ...... 461 Background...... 461 Part of the inspiration for XUI...... 461 Description of the Existing system ...... 462 Room for improvement...... 462 The XUI experience ...... 463 Comparisons...... 463 Results ...... 464 Features ...... 464
SECTION VI 471 Appendix A — Architecture ...... 473 The XUI Solution ...... 473 Project support for coordination of resources ...... 474 Factory methods to remove repetition ...... 474 Factories to target different devices/platforms ...... 475 Full API access ...... 475 Layered approach to styles ...... 475 Modular extension ...... 476 Page management...... 476 XML as a shortcut to declaring content...... 477 Proxies for event management ...... 478 Input validation...... 478 Data modelling ...... 479 Data loading ...... 479 Data binding ...... 480 Database access ...... 481 Services and communication...... 481 Routing ...... 482 Resource Loading...... 482 MVC pattern for separation of concerns ...... 483
• • • • 13 • • 1 – Table of Contents Carousel, User Guide
Appendix B — Java language ...... 485 Don’t forget you can use Java ...... 485 Object types ...... 485 Inheritance ...... 486 Package Names ...... 486 Classloaders...... 487 Reflection ...... 488 Encodings...... 488 Annotations ...... 489 Learning Java ...... 490 Further Resources ...... 491 Appendix C — Startup properties ...... 493 Properties ...... 493 Appendix D — Embedded support ...... 497 Choosing the JDK ...... 497 Choosing the right libraries...... 497 Rebuilding the libraries...... 497 Removing the shutdown hooks...... 497 Real-time support...... 498 Appendix E — Reference...... 499 Component attributes ...... 499 Appendix F — Upgrading...... 505 Projects ...... 505 Projects ...... 505 XPage split...... 506 ComponentAdapters...... 506 Widening of methods ...... 506 Page references ...... 506 New features ...... 506 Deprecation...... 507 Omitted components...... 507 Appendix G — Building XUI ...... 509 Accessing SVN ...... 509 Preparing to build...... 510 Building...... 510 Appendix H — Kalideoscope for Eclipse ...... 511 Installing the plugin ...... 511 Creating a new project...... 511 Accessing SVN ...... 514 Preparing to build...... 515 Building...... 515 Appendix I — Widgets Sets ...... 517 Widgets sets ...... 517 Swing ...... 517 AWT...... 517 SWT ...... 517 HTML...... 517 J#...... 518
• • • 14 • • • Carousel, User Guide 1 – Table of Contents
Others...... 518 Widget abstraction ...... 518 Portable coding...... 518 Design considerations ...... 518 Modularity ...... 518 Finding a JVM ...... 518 Appendix J — What’s new in version 3.0 ...... 519 New features...... 519 New XUI features...... 519 New Kalideoscope features ...... 530 New Carousel features ...... 533 Upgrading your project...... 534 INDEX 535
• • • • 15 • • 1 – Table of Contents Carousel, User Guide
• • • 16 • • • SECTION I BACKGROUND 1 – Carousel, User Guide
• • • 18 • • • 1 – About this book Carousel, User Guide
1 About this book
This user guide provides information on using and developing applications with Carousel. The guide is organized into an number of sections. Section I Begins with an introduction to Carousel, its history and its position on the technology landscape. Section II Describes the Carousel 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. Xoetrope 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 Carousel. The Xoetrope web site also features a developer zone where you can find working applications and numerous technical articles.
User Guide Conventions
The documentation for Carousel uses the typefaces and symbols described in the table below to indicate special text.
Typeface Meaning
Monospace type Monospaced 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.
Table 1-1 — Document conventions
• • 19 • • • • 1 – About this book Carousel, User Guide
Typeface Meaning
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”.
Table 1-1 — Document conventions
XUI and Carousel
Carousel is a commercial platform from Xoetrope that extends the libraries and the editor facilities available in the open source XUI platform. Some sections of this user guide are specific to Carousel. Wherever sections are specific to the Carousel platform you will see a note is italics at the start of the section or chapter indicating that the following content refers only to Carousel. Carousel is built upon XUI and therefore anything that can be done with XUI can be applied just as well to Carousel. The commercial Carousel specific libraries begin with the com.xoetrope package name whereas the open source libraries begin with the net.xoetrope package.
Contacting Xoetrope developer support
Xoetrope 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 Xoetrope 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 Xoetrope’s developer support services, see our website at http:// www.xoetrope.com. 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.
• • • 20 • • • 2 – Introduction Carousel, User Guide
2 Introduction
Carousel is a Java and XML framework for creating applications with rich user interfaces. Carousel 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. Carousel also has a rich set of libraries, tools and wizards for rapid application development.
Note: XUI (pronounced zo’i) is the open source platform upon which Carousel is in part based. Carousel greatly extends XUI and includes many features and facilities not available in XUI. Much of this document can be used as a reference for XUI. Features and chapters specific to Carousel will be noted at the start of each affected section.
What is Carousel?
First and foremost Carousel is a framework for building Java and XML based applications. The aims of Carousel 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 Carousel is a small Java library that allows 'pages' of an application to be constructed from an XML declaration or from Java code. Carousel offers many add-ons and extensions including a graphical editor/IDE. Carousel is commercial software extending the free open source software (FOSS) editor offered under the Xoetrope Public License (identical terms to the MPL) as part of the XUI platform. This license allows for Carousel to be used in commercial applications without the restrictions of some of the more common OSS licenses.
• • 21 • • • • 2 – Introduction Carousel, User Guide
The relationship between XUI and Carousel is shown below. XUI builds upon the Java platform, Carousel builds upon XUI and in turn your application can build upon all of these technologies.
Figure 2-1 — Carousel-XUI-Java
Carousel 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 Carousel?
Carousel can be used to build a wide variety of applications just a few of which are listed below: • Simple form based applications • 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
• • • 22 • • • Carousel, User Guide 2 – Introduction
Carousel 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.
Figure 2-2 — Rich-client platform landscape
Carousel also includes many sector specific features and add-ons. The Xoetrope website has a full list of the add-ons and case studies highlighting how Carousel’s has been used to build specific solutions.
What does Carousel do?
In a nutshell Carousel 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 Carousel built applications. Furthermore, being Java and XML based, Carousel applications are portable so that they can run on a wide variety of devices and platforms.
• • • • 23 • • 2 – Introduction Carousel, User Guide
As a development platform Carousel leverages the usual drag and drop techniques to make building applications quick and easy. Carousel includes numerous widgets, library functions and wizards to make many common tasks simple. Simplicity really is the key to Carousel, so even if the feature or behavior you desire isn’t predefined it usually isn’t difficult to implement. As an open system Carousel 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 Carousel’s capabilities you can work directly with Java and XML or with third party tools. So, ultimately Carousel make the developer’s life easier.
How does Carousel work?
Carousel takes advantage of local computing resources. So, instead of going back and forth to a server a Carousel application can store and process information. 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 applications. 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.
Figure 2-3 — MVC Architecture
In addition to the implicit benefits of a rich-client framework Carousel 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 Carousel 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.
• • • 24 • • • Carousel, User Guide 2 – Introduction
The Carousel 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. Carousel 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 Carousel manages to deliver similar behavior and benefits in a rich-client framework. Carousel however achieves this while leveraging the Java platform, in the process delivering efficient, modular and reusable components. Much of Carousel’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 Fixing a bug in say the collaborate on a project with perhaps one concentrating on the user interface and visual side business logic is while another may be responsible for business considerably easier logic or data processing. Whether this is actually a realizable business benefit is a moot point, but in when you don’t risk the long-run the benefits are very real. In a maintenance mode it may be many months breaking the user since a particular feature or aspect of an interface. 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. Carousel 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. Carousel however aims to make the process of building and maintaining applications as simple as possible. Using the techniques described above Carousel 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.
• • • • 25 • • 2 – Introduction Carousel, User Guide
Licensing
Carousel is available under the open source GPL license which allows developers to download and try out the software. A GPL version of Carousel may also be included in another GPL application, but for commercial uses most users will want a commercial license. Commercial licenses are available from Xoetrope without the restrictions of the GPL. XUI is available under the Xoetrope Public License (XPL - identical to the Mozilla license in all but name, as required by the MPL). Any enquiries about licensing can be addressed to [email protected].
• • • 26 • • • 3 – Why Carousel? Carousel, User Guide
3 Why Carousel?
Carousel started out as a simple editor designed to make life easier for the developer. The XUI library had helped make construction of the user interface (UI) simple and in Carousel this ease of use had been extended to include many of the features needed to build rich enterprise level applications. The modular nature of the framework meant that inevitably there was some configuration required, Carousel was therefore born to assist in the setup and configuration of such applications. Over time Carousel has developed to include a range of advanced features and tools that ease the task of building modern applications.
Background
Carousel 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 Carousel 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. Carousel 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.
• • 27 • • • • 3 – Why Carousel? Carousel, User Guide
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 XUI library at the core of Carousel 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. Carousel 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.
• • • 28 • • • Carousel, User Guide 3 – Why Carousel?
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 The mix of HTML, applications for some time. Although forms can be considered a workhorse of data capture, their use as JavaScript and server the de facto user interface is not particularly well side technologies can suited to the demands of modern business processes or for more complex application design. It is common for rapidly lead to applications to comprise whole sets of forms and much of their value comes from relating the information on complex technology 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.
• • • • 29 • • 3 – Why Carousel? Carousel, User Guide
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.
Figure 3-1 — The client-server landscape
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
• • • 30 • • • Carousel, User Guide 3 – Why Carousel?
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 Carousel?
Carousel 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, Carousel offers many advantages in terms of ease of development, Carousel is designed to performance and portability. These advantages lead directly to reduced development and improve the way in maintenance costs. The rapid development process that Carousel enables also means that you which forms and internet may have more time to devote to improving applications can be built. content and creativity. The open source XUI framework addresses many of the problems discussed above and then some more. Carousel adds many additional widgets for building applications and business sector specific features and wizards, not found in the basic open source platform. Carousel also provides client-server support so that applications can more easily work within a corporate environment and communicate with back-end services. Carousel 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.
• • • • 31 • • 3 – Why Carousel? Carousel, User Guide
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 Carousel 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 Carousel are worth using on their own not to mention using them in concert with other Carousel features. Take for example Carousel’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. Carousel is a modular system so you can employ just one feature like this on its own, mixing and matching to your needs. Carousel, can therefore be integrated into new, old or legacy applications without difficulty. For legacy applications the Carousel framework can provide a convenient way of adding new and modern features to what may otherwise be tired looking applications. Above all Carousel is designed by programmers for programmers.
• • • 32 • • • 4 – Related technologies Carousel, User Guide
4 Related technologies
Carousel is built upon an open architecture and hence it can support a wide variety of technologies. Equally Carousel 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. Carousel 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 Carousel.
Carousel 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 Carousel 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 Carousel is easy to read and understand. Separation of the scripting means that The full power of Java the XML is clean and compact; ideal for offloading to a graphic designer if so desired. The Java code is available to a used in place of the XML can run in the browser’s Carousel application. native environment and is pure code, again no mixing of content so from the programmer’s perspective this too is desirable.
Carousel 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
• • 33 • • • • 4 – Related technologies Carousel, User Guide
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.
Carousel versus Java
So why not program in Java? Carousel’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 Carousel can bring we do not mean to suggest it can do everything. Java is a very powerful programming platform and Carousel has been designed to work with Java and therefore the full power of Java is available to a Carousel application. Careful coding means that Carousel 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 Carousel; 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 Carousel’s technology is available on a wide range of JVMs. Despite the overlap between Carousel and some of the newer Swing features we plan to support emerging technologies as they mature and become available, particularly where they are standardized. Carousel 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 Carousel application as a component within a run-o-the-mill Swing application. Carousel 3.0 also extends the Swing widget support, making use of third party Swing components very simple. So what does all this mean? Well Carousel and XUI play very well with Swing and standard Java desktop technologies. For the most part you can view Carousel as a code saving technology for Swing, and after that you can use the normal Swing coding conventions.
Carousel 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
• • • 34 • • • Carousel, User Guide 4 – Related technologies
on the whole they fall short of offering the type of rich client that can be built with Carousel and the client side technologies outlined above. Of course most modern applications rely on a server side process to implement key business functions. Carousel is no different and in this sense it is Not everyone has the complementary to server side technologies. However Carousel relieves the server of the luxury of starting with challenges involved in much client state a clean slate, Carousel information. Carousel can integrate with business components on doesn’t necessitate such the backend via a variety of protocols and processes, an approach. be that simple HTML forms, J2EE, Soap or .Net. Carousel’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 Carousel 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). Carousel versus .NET Microsoft’s .Net platform has been gaining widespread coverage and Microsoft has made some forays into the forms market. Carousel 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, Carousel being an open source platform gives you plenty of options. Carousel 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 Carousel 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 Carousel add-on libraries published by Xoetrope. Carousel 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 Carousel. 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, Carousel 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 Carousel in many ways and
• • • • 35 • • 4 – Related technologies Carousel, User Guide
provides many of the same features and facilities so we take this as an endorsement of the Carousel architecture. In many ways Carousel 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 Carousel libraries can render and interoperate with XForms. Carousel 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 Carousel to read formats such as XForms, Infopath and XAML. Carousel 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 XUI and Carousel. 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 XUI or Carousel 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 Carousel 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 Carousel may be the better option. What may surprise some, who wrote of Java as slow and clunky, is that a Java application like Carousel can be visually attractive, with lots of rich user interface features and great performance. Where that visual impact is important Carousel is also the better option.
Other issues
The technology used in Carousel 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 Carousel does not necessitate such an approach. The open architecture employed by Carousel makes it easy to use Carousel when and where possible. This may simply be the use of Carousel to cleanup some code or it may be more extensive. Carousel 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. Carousel can greatly simplify this code by allowing much of the plumbing to be removed or hidden. Not
• • • 36 • • • Carousel, User Guide 4 – Related technologies
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 Carousel 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. Carousel’s support for new technologies like POJOs, data binding and modern user interface components also mean that Carousel 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 Carousel 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 Carousel promotes a clean architecture by separating development roles and concerns. As has already been remarked Carousel makes business logic stand out by removing plumbing code. Carousel 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. Carousel not only supports this capability but provides sophisticated mechanisms for handling remote communications, routing and fault tolerance. Added to this is Carousel’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. Carousel stylesheets facilitate this rebranding without change to the code. Within Carousel 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 Carousel 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
• • • • 37 • • 4 – Related technologies Carousel, User Guide
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).
• • • 38 • • • SECTION II INTRODUCTION 4 – Carousel, User Guide
• • • 40 • • • 5 – Installing Carousel Carousel, User Guide
5 Installing Carousel
Carousel can be installed in a number of ways depending on the intended usage. At its simplest Carousel can be downloaded and used as is. At the other end of the scale Carousel requires a number of installations to access all features.
Downloading Carousel
Carousel can be downloaded from the Xoetrope website at http://www.xoetrope.com. 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 Carousel that you need. As a registered user you will be able to download updates to Carousel on a regular basis as new features are added or enhanced. Typically a Carousel licence allows such downloads for a period of one year. Along with previous downloads we will allow access to a number of earlier versions of the software.
Carousel components
Carousel 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.
Component Usage
Carousel Carousel can be used with just about any Java development environment. All you need do to begin working with Carousel is add the carousel.jar module to your project’s classpath. To use Carousel 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 Carousel 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.
Table 5-1 — Carousel modules
• • 41 • • • • 5 – Installing Carousel Carousel, User Guide
Component Usage
Eclipse plug-in module An Eclipse plug-in provides wizards for creation of projects and pages and supports editing and debugging of XUI projects.
SurveyManager This is a tool for creation and management of survey and questionnaire applications and applets. With a little imagination this module can be used for other types of questionnaires including e- learning, tests and quiz.
ProjectManager The project manager library adds a set of classes to assist in project based applications. Projects for creation of product selection, ordering, documentation and so forth can be supported by this library. The module also contains a wizard to assist in the configuration of such projects.
Table 5-1 — Carousel modules
XUI components
Carousel is an extension of the open source XUI platform. Carousel includes XUI and the KALIDEOSCOPE but it is also possible to download XUI directly from SourceForge (http:// xui.sourceforge.net). On the files area you can obtain a number of variants of the XUI 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:
Component Usage
XuiCore.jar This Jar contains all the class files needed for core XUI development using the AWT widget set.
XuiCoreSwing.jar This Jar contains all the class files needed for core XUI development using the Swing widget set.
XuiOptional.jar Extensions to the core including database bindings and more.
XuiALL.jar The complete set of class files and resources including the standalone (non NetBeans editor) and support for both Swing and AWT widget sets.
xui-src-
Table 5-2 — Carousel modules
The XUI Jars are compiled for a number of JDKs and accordingly have slightly different feature sets, for example only the JDK 1.5 and later versions feature the Synth Look and Feel as that feature was introduced as part of the Java platform with JDK 1.5. The fullest feature is
• • • 42 • • • Carousel, User Guide 5 – Installing Carousel
the JDK 1.5 version and the version with the least features is the JDK 1.1.8 version (which runs on virtually all JVMs). 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 XUI. The XUI 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
Carousel includes a NetBeans plug-in for rapid development. Most of the features of Carousel 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 Carousel NetBeans can be downloaded from http://www.netbeans.org. The NetBeans site also contains extensive documentation of installing and using the IDE.
• • • • 43 • • 5 – Installing Carousel Carousel, User Guide
Loading Carousel
Once Carousel 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.
Figure 5-1 — The NetBeans update centre.
Then choose the ‘Install Manually Downloaded Modules (.nbm files)’ option. Select the three NetBeans modules: net-xoetrope-xui.nbm, net-xoetrope-editor.nbm
• • • 44 • • • Carousel, User Guide 5 – Installing Carousel
and Carousel.nbm. The first of these are the module for the open source XUI and KALIDEOSCOPE modules and the third is the module for Carousel’s add-on features.
Figure 5-2 — Module selection.
• • • • 45 • • 5 – Installing Carousel Carousel, User Guide
When the NetBeans module has been successfully installed the Carousel menu item will appear on the main menu.
Figure 5-3 — The Carousel menu with NetBeans and the Language Editor.
If you are just installing XUI and Kalideoscope, then you can check that the installation has succeeded by check that the New XUI Project wizard is available from the File | New | Project menu option.
Installing the samples
A number of samples are available on the Xoetrope 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 Carousel are listed below:
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.
Table 5-3 — Sample applications
• • • 46 • • • Carousel, User Guide 5 – Installing Carousel
Sample Description
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 Carousel.
Table 5-3 — Sample applications
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.xoetrope.com). The Xoetrope 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 XUI and Carousel 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 XUI project wizard under the File | New | Other dialog. At present there is no Eclipse version of the Carousel plugin.
• • • • 47 • • 5 – Installing Carousel Carousel, User Guide
• • • 48 • • • 6 – A quick tour of the editor Carousel, User Guide
6 A quick tour of the editor
This chapter provides a quick tour of the Carousel 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 Carousel templates are unsurprisingly listed under the Carousel heading. To access the templates choose File | New from the main menu. The templates included in Carousel are as follows: • New Project: Creates a the stub of a new project • New Page: Creates a blank page within the current project
Figure 6-1 — The new project wizard.
• • 49 • • • • 6 – A quick tour of the editor Carousel, User Guide
To create a new project choose the New Project template. The template is shown below.
Figure 6-2 — The new project wizard, step2 - choose the project location.
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.
Figure 6-3 — The new project wizard, step 3 - the project settings.
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
• • • 50 • • • Carousel, User Guide 6 – A quick tour of the editor
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 Carousel). 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.
Figure 6-4 — The new project wizard, step 4 - the frameset settings.
• • • • 51 • • 6 – A quick tour of the editor Carousel, User Guide
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.
Figure 6-5 — The new project wizard, step 4 - the file settings.
The final page of the new project wizard provides a place to configure some add-ons for Carousel. 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.
Figure 6-6 — The new project wizard, step 4 - customize and complete.
• • • 52 • • • Carousel, User Guide 6 – A quick tour of the editor
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.
Figure 6-7 — The project view
• • • • 53 • • 6 – A quick tour of the editor Carousel, User Guide
A number of directories are created under the project’s main directory or folder. The directories are as follows:
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.
Table 6-1 — Project folders
• • • 54 • • • Carousel, User Guide 6 – A quick tour of the editor
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.
Figure 6-8 — The project settings editors.
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 File 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 Carousel 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
• • • • 55 • • 6 – A quick tour of the editor Carousel, User Guide
the page and its location. The page should be placed in the ‘pages’ subdirectory of the project.
Figure 6-9 — The new page wizard.
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.
• • • 56 • • • Carousel, User Guide 6 – A quick tour of the editor
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.
A BC
Figure 6-10 — Editing a page.
At this point it is worth noting some of the overall features 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.
Table 6-2 — Areas of the editor
• • • • 57 • • 6 – A quick tour of the editor Carousel, User Guide
Section Description
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.
Table 6-2 — Areas of the editor
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 Carousel. As a minimum there will be several core components shown. These are:
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 Carousel provides support for validation of input data using edit fields.
Table 6-3 — Component palette icons
• • • 58 • • • Carousel, User Guide 6 – A quick tour of the editor
Icon Component description
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.
Table 6-3 — Component palette icons
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 Carousel 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
• • • • 59 • • 6 – A quick tour of the editor Carousel, User Guide
configuration, for example if an AWT application has been chosen then the Swing widgets will not be available.
Styles
Styles in Carousel 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.
Figure 6-11 — The style chooser.
Styles can also be changed by simply changing the style file or by changing individual styles (by right clicking on the style palette). Carousel 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. Carousel 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 Carousel declares such links in the page’s XML. You can
• • • 60 • • • Carousel, User Guide 6 – A quick tour of the editor
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
Figure 6-12 — The XML edit for a page. 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 Hold the CTRL key one of the predefined functions if appropriate. Once you have added a function and wish to jump while you click on an to the source code, just hold the CTRL key while you click on the event handler and this will open existing event handler the source code editor instead of editing the to open the source code handler name. In later chapters we will see just how to begin editor coding custom business logic.
• • • • 61 • • 6 – A quick tour of the editor Carousel, User Guide
XML
As a technology both the KALIDEOSCOPE and Carousel make extensive use of XML. XML is used to describe a wide variety of things in Carousel, including the user interface. The XML for a UI can be displayed by choosing the XML tab for that page. Furthermore Carousel 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 Carousel’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 XUI 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. Carousel 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. Carousel generates a stub of a JNLP file that can be used by such plug-ins.
• • • 62 • • • 7 – Running applications Carousel, User Guide
7 Running applications
Within Carousel 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 Carousel applications depending upon how the applications will be deployed. All of these start-up sequences start by invoking the XApplet class and eventually follow a generic initialization process. Some minor variations are accommodated by the XApplet class to handle the different ways of setting start-up parameters and the differences in the classloaders. When starting a Carousel application the choice of the XApplet class is important as this dictates whether or not the Carousel framework constructs Swing or AWT components. If the net.xoetrope.awt.XApplet class is used then AWT components are used, whereas if the net.xoetrope.swing.XApplet 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 XApplet 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 XApplet class.
java -cp XuiCore.jar net.xoetrope.awt.XApplet startup.properties
Code Sample 7-1 — Command Line startup command
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 XApplet is used to start the application (net.xoetrope.awt for the net.xoetrope.awt.AXpplet class and net.xoetrope.swing for the net.xoetrope.swing.XApplet 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 Carousel each time the project is built, however Carousel actually stores the values is the project.xui file for its own use.
• • 63 • • • • 7 – Running applications Carousel, User Guide
Java Web Start Start-up Sequence
To run a Carousel 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:
Code Sample 7-2 — Java Web Start (JNLP) startup code
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:
Code Sample 7-3 — Basic applet tag
Again the Jar file should be signed for distribution and placed on the webserver.
• • • 64 • • • Carousel, User Guide 7 – Running applications
Generic Initialization
Once the XApplet 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 XProject 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 XApplet) is when the first page is created, by embedding code in that page's pageCreated or pageActivated method. Carousel 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. Carousel 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 XApplicationContext class which is used to setup the common infrastructure by the XUI framework. An example of this is the sample application that
• • • • 65 • • 7 – Running applications Carousel, User Guide
integrates XUI into the run-of-the-mill Swing application as a panel embedded in that application (see “Embedded applications” on page 198).
Startup file stubs
Carousel 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.
Figure 7-1 — Choose the main class.
The XUI libraries should be on the project classpath and therefore Eclipse should be able to locate the XApplet 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.
• • • 66 • • • 8 – Getting started Carousel, User Guide
8 Getting started
In this chapter we will see how to build some simple Carousel applications using both Java and XML. We will see all the features needed to build a fully functional application within Carousel in action. The chapter provides an introduction to the setup mechanisms used within a Carousel 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 (“Learning Java” on page 490).
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 Carousel 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:
Code Sample 8-1 — Page user-interface declaration
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
• • 67 • • • • 8 – Getting started Carousel, User Guide
start class in this case is the entry point to the Carousel framework or library. It contains the following command:
java -classpath .;XuiCore118.jar; net.xoetrope.awt.XApplet
Code Sample 8-2 — Command-line startup
The XuiCore118 jar is the minimum level of library support that can be used with Carousel. It is a version of the Carousel 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:
java -classpath .;XuiCoreSwing.jar; net.xoetrope.swing.XApplet
Code Sample 8-3 — Command-line startup for Swing
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 Carousel 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 Carousel 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:
import net.xoetrope.xui.*; import net.xoetrope.awt.*;
public class Home extends XPage { public Home() { addComponent( XPage.LABEL, 32, 37, 100, 29, "Hello World" ); } }
Code Sample 8-4 — Java UI class
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.
• • • 68 • • • Carousel, User Guide 8 – Getting started
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:
javac Home.java
Code Sample 8-5 — Command-line startup for compilation of the Java class
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 XUI 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 Carousel it is not normal. Carousel can use many different resources and settings to configure and run an application. The HelloWorld examples relied on default settings for most of the Carousel configuration. Normally Carousel applications are configured via a startup properties file. This file can be specified as a command-line argument:
java -classpath .;XuiCore118.jar; net.xoetrope.awt.XApplet startup.properties
Code Sample 8-6 — Command-line startup using a startup properties file
Or, if no command-line argument is provided Carousel will look for 'startup.properties'. (And remember that the startup file is regenerated by Carousel whenever an application is saved. Carousel itself uses a file project.xui to store the properties that are written to the startup.properties file). It is also useful to separate the various components of an Carousel project into separate sub- Carousel uses a file directories. Thus the XML declarations for pages are put into the 'pages' sub-directory, while the ‘project.xui’ to store the Java source files are places in the 'src' sub- directory. Other resources such as images are properties that are placed in the 'resources' sub-directory and written to the finally localization files are placed in the 'lang' subdirectory. ‘startup.properties’ file. When using Carousel (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.
• • • • 69 • • 8 – Getting started Carousel, User Guide
The startup file contains settings that control much of how Carousel is configured. Some of the more commonly used configuration settings are shown below and could be used for the HelloWorld example.
CenterWin=true ClientHeight=480 ClientWidth=640 StartClass=Home Title=A simple HelloWorld example StyleFile=styles.xml UseWindow=true StartPackage=xui.projects.helloworld
Code Sample 8-7 — Startup properties
The purpose of these settings is as follows:
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 Carousel should look for a class called 'xui.projects.helloworld.Home.class'.
UseWindow (true|false) Tells Carousel 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.
Table 8-1 — Startup parameters
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.
• • • 70 • • • Carousel, User Guide 8 – Getting started
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:
Code Sample 8-8 — An address form/page declared in XML
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. Carousel 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.
• • • • 71 • • 8 – Getting started Carousel, User Guide
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:
Code Sample 8-9 — A typical style file
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:
Code Sample 8-10 — An XML page using styles
• • • 72 • • • Carousel, User Guide 8 – Getting started
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. Carousel 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. Carousel 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:
Code Sample 8-11 — A validation rules file
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:
Code Sample 8-12 — An XML page using validation rules
• • • • 73 • • 8 – Getting started Carousel, User Guide
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. Carousel 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 Carousel 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.
Figure 8-1 — Binding data to multiple pages.
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.
• • • 74 • • • Carousel, User Guide 8 – Getting started
Normally the links between the components on user interface pages are declared as data bindings.
Code Sample 8-13 — An XML page binding data to the user-interface components
Carousel will automatically update the form so that it displays the data from the model whenever the page is displayed. Carousel 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:
Code Sample 8-14 — A sample dataset reference
• • • • 75 • • 8 – Getting started Carousel, User Guide
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:
Code Sample 8-15 — A sample dataset
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. Carousel relies on Java for its programming and at the core of this is the XPage class. Each page in Carousel 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:
package xui.projects.addresses;
import net.xoetrope.xui.*; import net.xoetrope.swing.*;
public class Welcome extends XPage { public Welcome() { }
public void next() { } }
Code Sample 8-16 — A simple Java class with an event handler
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:
Code Sample 8-17 — Referencing the Java class in the page’s XML declaration
• • • 76 • • • Carousel, User Guide 8 – Getting started
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:
Code Sample 8-18 — Adding an event handler to the page’s XML
The complete form now appears like this:
Code Sample 8-19 — The complete form
Changing pages
One of the most common tasks in building an application is providing a means of navigating around the application and since the XPage is the basis of all pages in an Carousel application the XPage class provides methods for changing pages.
• • • • 77 • • 8 – Getting started Carousel, User Guide
To show the second page we modify the 'next' method.
package xui.projects.addresses;
import net.xoetrope.xui.*; import net.xoetrope.swing.*;
public class Welcome extends XPage { public Welcome() { }
public void next() { pageMgr.showPage( “pageTwo” ); } }
Code Sample 8-20 — Implementing the event handler
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 Carousel 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).
Figure 8-2 — The page display lifecycle.
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
• • • 78 • • • Carousel, User Guide 8 – Getting started
(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.
• • • • 79 • • 8 – Getting started Carousel, User Guide
• • • 80 • • • 9 – Projects Carousel, User Guide
9 Projects
Carousel 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 Carousel application.
A project’s role
A project is a collection of the resources used in an application. The project object within a Carousel application manages these resources and provides many facilities for loading and storing resources.
Figure 9-1 — The project acts as a central resource manager
As many of the components in a Carousel 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.
• • 81 • • • • 9 – Projects Carousel, User Guide
Carousel 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 XUI had used singletons (classes containing a static self reference) to provide easy access to key resource classes such as the XStyleManager 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. XUI 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 Carousel or Kalideoscope 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.
Figure 9-2 — The NetBeans New Project Wizard
• • • 82 • • • Carousel, User Guide 9 – Projects
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. Carousel’s project files are contained in the root of the project and have a .xui extension. The file is an XML file, but fortunately Carousel provides an editor for all the project’s settings:
Figure 9-3 — Editing project properties
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:
Folder Usage
Pages Stores the XML page descriptions
Lang Stores the language files used for translation of the application.
Table 9-1 — Project folders
• • • • 83 • • 9 – Projects Carousel, User Guide
Folder Usage
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.
Table 9-1 — Project folders
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:
Property Usage
Project name This is the name by which the project is identified within Carousel.
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.
Table 9-2 — General properties
• • • 84 • • • Carousel, User Guide 9 – Projects
Property Usage
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 Carousel 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 Carousel 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.
Table 9-2 — General properties
Frames setup Carousel 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.
• • • • 85 • • 9 – Projects Carousel, User Guide
Files Setup Rather than try to shoehorn everything into one configuration file Carousel 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 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 Carousel 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.
Table 9-3 — File settings
• • • 86 • • • Carousel, User Guide 9 – Projects
File Usage
Look and feel The look and feel of the application only requires a choice of files if the Synth look and feel is employed.
Table 9-3 — File settings
Extensions setup Carousel 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 Carousel 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 Carousel 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. Carousel itself uses this facility for some of its optional parameters and you will see this project editing facility referenced in later chapters.
• • • • 87 • • 9 – Projects Carousel, User Guide
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 Carousel 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 Carousel 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 XProjectManager class which provides a static accessor:
XProject currentProject = XProjectManger.getCurrentProject();
Table 9-4 — Locating a project
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 Carousel application the project is also often the first place to look when trying to find a particular resource. As of XUI 2.0 the XPage class includes a reference ‘project’ to the current project so the above code may no longer be necessary in most situations.
• • • 88 • • • 10 – Designing pages Carousel, User Guide
10 Designing pages
Once you have setup a project the next task is to create some content, and this means adding pages to you application. Carousel 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 XUI 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 XUI Page... option. Once the new page has been created it is opened in the page designer.
Opening the page
The pages created by Carousel 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 XUI page designer. Eclipse users
• • 89 • • • • 10 – Designing pages Carousel, User Guide
may also have to open the Kalideoscope 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 Carousel 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 “The page designer” on page 57. 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.
Figure 10-1 — Edit a page within a frameset
• • • 90 • • • Carousel, User Guide 10 – Designing pages
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.
Figure 10-2 — Set the preferred page size
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 Carousel 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 Carousel (see “Working with components” on page 309). 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.
• • • • 91 • • 10 – Designing pages Carousel, User Guide
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)
Figure 10-3 — Multiple component selections
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.
• • • 92 • • • Carousel, User Guide 10 – Designing pages
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
• • • • 93 • • 10 – Designing pages Carousel, User Guide
“Guides” on page 145). You can also add columns and rows for regular form layouts by choosing the Setup columns... option.
Figure 10-4 — Setting up guides
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 “Layout” on page 143. 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 XUI’s own layout managers may respect the positioning defined with the page designer, but this behavior varies from layout manager to layout manager.
• • • 94 • • • Carousel, User Guide 10 – Designing pages
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.
Figure 10-5 — A property sheet
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
• • • • 95 • • 10 – Designing pages Carousel, User Guide
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
• • • 96 • • • Carousel, User Guide 10 – Designing pages
using the property sheet or with the in-situ property sheet. Kalideoscope also includes a style palette, located in the same area as the property sheet (but on the tab marked ‘Styles’ ).
Figure 10-6 — The Style palette
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.
• • • • 97 • • 10 – Designing pages Carousel, User Guide
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 XUI framework takes care of the event handling plumbing.
Figure 10-7 — A new event handler
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.
• • • 98 • • • Carousel, User Guide 10 – Designing pages
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 XUI helper... option. Choosing this option pops up a dialog that lists some common XUI code fragments (this list can be extended).
Figure 10-8 — Insert a code fragment
Working with data
Carousel supports many data formats and data source (including customized datasources) and the Carousel 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 Carousel | 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” on page 159 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.
• • • • 99 • • 10 – Designing pages Carousel, User Guide
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.
Figure 10-9 — XML view of the page designer
• • • 100 • • • Carousel, User Guide 10 – Designing pages
Working with included pages
An advanced feature of XUI, 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.
• • • • 101 • • 10 – Designing pages Carousel, User Guide
• • • 102 • • • 11 – Building pages with XML Carousel, User Guide
11 Building pages with XML
As we have already seen, Carousel 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 Carousel 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 Carousel framework when it constructs a page.
Figure 11-1 — A simple form
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 Carousel 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.
• • 103 • • • • 11 – Building pages with XML Carousel, User Guide
A snippet of the XML file for the form shown above is shown below.
Code Sample 11-1 — A basic address page described in XML
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. Carousel constructs an instance of a Java class, the XPage class by default and populates it with the components you have specified in the XML.
Figure 11-2 — Constructing a form from 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 XQuestionPage 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.
• • • 104 • • • Carousel, User Guide 11 – Building pages with XML
Thus one page can be composed of multiple pages.
Code Sample 11-2 — An advanced page decription showing repeating elements, embedded data etc
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
Carousel 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 Usage
XButton A simple push button type. [Button]
XCheckbox A check box normally used to indicate options [Checkbox]
XComboBox A drop down list used to display a fixed set of choices or options.[Combo]
XDialog 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]
XEdit A basic input field. [Edit]
XHotspotImage An image that when clicked in certain areas triggers events. [Hotspot]
XImage A simple image display component. [Image]
XImageMap An extension of XImage that allows querying of the mouse coordinates. [ImageMap]
XLabel A simple static piece of text that does not permit user interaction. Text can be flowed over multiple lines. [Label]
Table 11-1 — Component types
• • • • 105 • • 11 – Building pages with XML Carousel, User Guide
Component Usage
XList A simple list. [List]
XMenu A menu, either drop down or popup, depending on the context. [Menu]
XMenuBar A menu bar for holding menus in an application. [MenuBar]
XMenuItem An individual menu item. Selection triggers an action. [MenuItem]
XMessageBox A popup dialog for display of simple messages, warnings or errors. [constructed implicitly by calling showMessage]
XMetaContent A text field that can display some simple meta content, altering typefaces and embedding tables and so on. [MetaContent]
XPanel A container that can holder other components. The container can be displayed with a border and is considered to 'own' its children. [Panel]
XPassword An edit field that masks the user input so that an observer can not see what has been keyed. [Password]
XRadioButton 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.
XScrollPane A container that allows scrolling of its content. [ScrollPane]
XScrollableMetaContent A version of the meta content control that supports scrolling of its content. [ScrollabelMetaContent]
XTab An individual Tab for a tab pane. [Added implicitly when a child panel is added to a tab panel]
XTable A component that displays tabular data. [Table]
XTabPanel A contain that allows access to multiple child panels by selection of a tab. [TabPanel]
XTextArea A multiline text field. [TextArea]
Table 11-1 — Component types
More details of these controls can be found in the appendix.
• • • 106 • • • Carousel, User Guide 11 – Building pages with XML
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
Code Sample 11-3 — Specifying a button
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.
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.
Table 11-2 — Common component properties
• • • • 107 • • 11 – Building pages with XML Carousel, User Guide
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.
Code Sample 11-4 — A panel
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 XPage and is positioned using a layout manager. Navigation commands such showPage have been enhanced to take the name of the target frame.
Code Sample 11-5 — A 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.
• • • 108 • • • Carousel, User Guide 11 – Building pages with XML
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.
Figure 11-3 — A form with an included file for navigation buttons.
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
Code Sample 11-6 — The included file
Now, in order to use this screen we simply use the Include tag within another screen.
Code Sample 11-7 — The include statement
And to attach events to the buttons we add events for each screen.
Code Sample 11-8 — Attaching an event
Carousel 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 elements1
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.
• • • • 109 • • 11 – Building pages with XML Carousel, User Guide
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.
Code Sample 11-9 — The complete example
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 Carousel 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.
Figure 11-4 — A border layout in action.
1.A feature of Carousel only and XUI 2.0 and later
• • • 110 • • • Carousel, User Guide 11 – Building pages with XML
The new method takes one parameter, the layout manager constraint.
public Component addComponent( int type, Object constraint );
Code Sample 11-10 — Adding a component via Java (API)
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
public LayoutManager addLayout( layoutType );
Code Sample 11-11 — Specifying a layout (API)
The layoutType can be one of the XPage constants:
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.
Table 11-3 — Layout types
In the XML page descriptions layout managers can be specified for both pages and panels. An example is shown below. For both the XPage 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
• • • • 111 • • 11 – Building pages with XML Carousel, User Guide
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 XLayoutHelper class.
Code Sample 11-12 — A simple border layout
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 “Layout” (see page 143).
Validations
Validations are rules that can be used to check that input is correct. Carousel 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:
Code Sample 11-13 — A validation rules file
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.
• • • 112 • • • Carousel, User Guide 11 – Building pages with XML
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:
Code Sample 11-14 — A complete example
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 Carousel 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 XUI 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 XPage 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 XPage so that calls specified in the XML can be intercepted by the custom class.
public class EventHandling extends XPage
Code Sample 11-15 — Specifying a support class/page
• • • • 113 • • 11 – Building pages with XML Carousel, User Guide
The XML file now specifies this custom class as its base class and defines the two buttons which have attached events.
Code Sample 11-16 — Binding to a class
So now our Java code needs to handle the events by implementing the referenced methods:
public void nextPage() { // Do something here... XPageManager.showPage( getAttribute( "next" ) ); }
public void cancel() { XButton button = ( XButton ) findComponent( "btnCancel" ); button.setText( "Clicked!" ); }
Code Sample 11-17 — Coding event handlers
On execution the XUI 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 (“Evaluated attributes and helpers” (see page 273)) 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 XUI 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
Carousel 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.
• • • 114 • • • Carousel, User Guide 11 – Building pages with XML
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 Carousel takes care of getting and saving the data. An example of this is shown below:
Code Sample 11-18 — Adding 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.
• • • • 115 • • 11 – Building pages with XML Carousel, User Guide
• • • 116 • • • 12 – Building pages with Java Carousel, User Guide
12 Building pages with Java
Building a Carousel 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 Carousel-Java page requires a class derived from XPage, the basis of all Carousel pages. The traditional HelloWorld example can thus be coded:
import net.xoetrope.xui.*;
public class HelloWorld extends XPage { public HelloWorld() { componentFactory.addComponent( LABEL, 10, 40, 90, 20, "Hello World" ); } }
Code Sample 12-1 — Hello World coded in Java
In the above example the page’s base class, XPage, 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 XPage 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).
• • 117 • • • • 12 – Building pages with Java Carousel, User Guide
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 Carousel the styles are defined in a styles file. The startup file should include a setting for a 'StyleFile' which points to the styles file.
UseWindow=false ClientWidth=240 ClientHeight=300 StartPackage=net.xoetrope.samples.simple StartClass=StyleSheet Title=Simple Form Sample StyleFile=stylesheetstyle.xml
Code Sample 12-2 — Startup Properties including a style file reference
To use the styles the component factory addComponent method signature has been extended to take an extra parameter, the name of the style. The XLabel components might all use the explicit style 'prompt', whereas the XEdit components are styled implicitly by including the XEdit 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.
componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Firstname:", "prompt" ); txtFirstname = (XEdit)componentFactory.addComponent( EDIT, 100, 10, 100, 20, "Joe" );
Code Sample 12-3 — Adding a label and an edit field
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.
btnOK = (XButton)componentFactory.addComponent( BUTTON, 40, 10, 60, 20, "Proceed" ); btnCancel = (XButton)componentFactory.addComponent( BUTTON, 110,10,60, 20, "Cancel", "BtnReverse" )
Code Sample 12-4 — Adding OK and Cancel buttons
• • • 118 • • • Carousel, User Guide 12 – Building pages with Java
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:
Figure 12-1 — A simple form
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 XPage, 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.
private void addEvents() { addTextHandler( txtCarCost, "changeAdvance" ); addTextHandler( txtDeposit, "changeAdvance" ); addMouseHandler( btnOK, "proceed" ); }
Code Sample 12-5 — Adding event handlers
The called methods must be declared as public void.
public void changeAdvance() { float cost = Float.parseFloat( txtCarCost.getText() ); float deposit = Float.parseFloat( txtDeposit.getText() ); txtAdvance.setText( String.valueOf( cost-deposit) ); }
public void proceed() { if ( wasMouseClicked() ) btnOK.setLabel( "Clicked!" ); }
Code Sample 12-6 — Simple event handlers
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 XPage.wasMouseClicked() method to determine if it was a ‘click’ which triggered the event. The mouse handler is
• • • • 119 • • 12 – Building pages with Java Carousel, User Guide
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:
private void addEvents() { addTextHandler( txtCarCost, "changeAdvance" ); addTextHandler( txtDeposit, "changeAdvance" ); addActionHandler( btnOK, "proceed" ); }
Code Sample 12-7 — Revised addition of event handlers
With this revision we can simplify the response method to remove the unnecessary checks.
public void changeAdvance() { float cost = Float.parseFloat( txtCarCost.getText() ); float deposit = Float.parseFloat( txtDeposit.getText() ); txtAdvance.setText( String.valueOf( cost-deposit) ); }
public void proceed() { btnOK.setLabel( "Clicked!" ); }
Code Sample 12-8 — Revised simple event handlers
If you do indeed need to do more analysis of the event which triggered the call you can access the event with the XPage.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 XTextBinding objects.
private void addBindings() { addBinding( new XTextBinding(txtFirstname, "customer/firstname") ); addBinding( new XTextBinding(txtSurname, "customer/surname") ); addBinding( new XListBinding(cmbAge, "customer/ageRanges") );
addBinding( new XListBinding(txtCarCost, "financial/carcost") ); addBinding( new XTextBinding(txtDeposit, "financial/deposit") ); }
Code Sample 12-9 — Inserting data bindings
• • • 120 • • • Carousel, User Guide 12 – Building pages with Java
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:
Code Sample 12-10 — Validation rules
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 XUI platform required an explicit call to setup the validation handling with a call to the 'setExceptionHandler' method of the XPage, this is no longer the case. To use the validations we need to bind the validation to the components with a call to addValidation.
addValidation( txtFirstname, "firstname" ); addValidation( txtSurname, "surname" ); addValidation( txtAge, "age" );
Code Sample 12-11 — Binding the validatin rules to input fields
Putting the complete example together we get:
package net.xoetrope.samples.simple;
import net.xoetrope.xui.*; import net.xoetrope.xui.data.*;
public class DataBinding extends XPage { XEdit txtFirstname, txtSurname, txtCarCost, txtDeposit, txtAdvance; XComboBox cmbAge; XPanel pnlClientInfo, pnlFinancialInfo, pnlButtons; XButton btnOK, btnCancel;
Code Sample 12-12 — The complete example
• • • • 121 • • 12 – Building pages with Java Carousel, User Guide
public DataBinding() { pnlClientInfo = (XPanel)componentFactory.addComponent( PANEL, 10, 10, 210, 80 ); pnlFinancialInfo = (XPanel)componentFactory.addComponent( PANEL, 10, 100, 210, 80 ); pnlButtons = (XPanel)componentFactory.addComponent( PANEL, 10, 200, 210, 40 ); pnlClientInfo.setDrawFrame( XPanel.BORDER_BEVEL ); pnlFinancialInfo.setDrawFrame( XPanel.BORDER_BEVEL ); pnlButtons.setDrawFrame( XPanel.BORDER_BEVEL ); componentFactory.setParentComponent( pnlClientInfo ); componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Firstname:", "prompt"); txtFirstname = (XEdit)componentFactory.addComponent( XEDIT, 100, 10, 100, 20 ); componentFactory.addComponent( LABEL, 5, 30, 90, 20, "Surname:", "prompt"); txtSurname = (XEdit)componentFactory.addComponent( XEDIT, 100, 30, 100, 20 ); componentFactory.addComponent( LABEL, 5, 50, 90, 20, "Age:", "prompt"); cmbAge = (XComboBox)componentFactory.addComponent( COMBO, 100, 50, 100, 20, null, "XEdit" );
componentFactory.setParentComponent( pnlFinancialInfo ); componentFactory.addComponent( LABEL, 5, 10, 90, 20, "Car cost:", "prompt"); txtCarCost = (XEdit)componentFactory.addComponent( EDIT, 100, 10, 100, 20 ); componentFactory.addComponent( LABEL, 5, 30, 90, 20, "Deposit:", "prompt"); txtDeposit = (XEdit)componentFactory.addComponent( EDIT, 100, 30, 100, 20 ); componentFactory.addComponent( LABEL, 5, 50, 90, 20, "Advance:", "prompt"); txtAdvance = (XEdit)componentFactory.addComponent( EDIT, 100, 50, 100, 20 ); txtAdvance.setEnabled( false );
componentFactory.setParentComponent( pnlButtons ); btnOK = (XButton)componentFactory.addComponent( BUTTON, 40, 10, 60, 20, "Proceed" ); btnCancel = (XButton)componentFactory.addComponent( BUTTON, 110, 10, 60, 20, "Cancel", "BtnReverse" ); addEvents(); addBindings(); }
public void pageActivated() { changeAdvance(); }
private void addEvents() { addTextHandler( txtCarCost, "changeAdvance" ); addTextHandler( txtDeposit, "changeAdvance" ); addActionHandler( btnOK, "proceed" ); }
private void addBindings() { addBinding( new XTextBinding(txtFirstname, "customer/firstname") ); addBinding( new XTextBinding(txtSurname, "customer/surname") ); addBinding( new XListBinding(cmbAge, "customer/ageRanges") ); addBinding( new XTextBinding(txtCarCost, "financial/carcost") ); addBinding( new XTextBinding(txtDeposit, "financial/deposit") );
}
public void changeAdvance() { float cost = Float.valueOf( txtCarCost.getText() ).floatValue(); float deposit = Float.valueOf( txtDeposit.getText() ).floatValue();
txtAdvance.setText( String.valueOf( cost - deposit) ); }
public void proceed() { btnOK.setLabel( "Clicked!" ); } }
Code Sample 12-12 — The complete example
• • • 122 • • • Carousel, User Guide 12 – Building pages with Java
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:
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 XButton 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.
Table 12-1 — Annotations
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 net.xoetrope.optional.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
• • • • 123 • • 12 – Building pages with Java Carousel, User Guide
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:
BuddyHelper buddy = new BuddyHelper( (XStyleFactory)componentFactory ); imgSingle = ( XEdit )buddy.addComponent( EDIT, 5, 100, 10, 200, 20, panelWidth, 28, "First Name", "", "prompt" ); imgDouble = ( XEdit )buddy.addComponent( EDIT, 5, 100, 30, 200, 20, panelWidth, 28, "Second Name", "", "prompt" );
Code Sample 12-13 — A buddy in use
Carousel 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 Carousel including the data model, events and so on and are available throughout the lifecycle of the application.
Other helpers
Carousel provides numerous helper classes aimed to simplify Java coding and to remove those verbose typecasting requirements.
NavigationHelper NavigationHelper extends XPage 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 XTableModelHelper A utility class to help construct a HTML like table structure XModelNodeHelper A helper to eliminate some typecasting with the XModel
Table 12-2 — Some helper classes
Logging
For most applications it is useful to use logging during the develop stage of a project. Carousel 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.
• • • 124 • • • Carousel, User Guide 12 – Building pages with Java
At startup the 'LogLevel' property is used to set the initial value. Carousel itself uses logging extensively and this can lead to verbose console output. To further help cut down on the logging Carousel 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:
if ( BuildProperties.DEBUG ) DebugLogger.logWarning( “Some log message” );
Code Sample 12-14 — Adding debug logging
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. Carousel 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 XuiALL.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 Carousel 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 Carousel application with Java it is necessary to derive the page from the XPage 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 Carousel 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 Carousel 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.
• • • • 125 • • 12 – Building pages with Java Carousel, User Guide
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. Carousel 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 Carousel, “Choosing how to install components” (see page 309) and accessed with the techniques described above. Since third party components are not built-in Carousel 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.
• • • 126 • • • SECTION III IN DEPTH 12 – Carousel, User Guide
• • • 128 • • • 13 – Styles Carousel, User Guide
13 Styles
Most applications try to use consistent styling and Carousel provides support for consistent coloring and typography through its style manager. Carousel'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. Carousel provides interactive visual tools to help design and use styles. Within Carousel 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.
Figure 13-1 — The style palette
• • 129 • • • • 13 – Styles Carousel, User Guide
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. Figure 13-2 — The style palette
Color schemes
Carousel extends the notion of styles by including a color scheme chooser to provide coherent sets of styles.
Figure 13-3 — The color chooser, choosing the colors
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.
• • • 130 • • • Carousel, User Guide 13 – Styles
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.
Figure 13-4 — The color chooser, setting up styles
• • • • 131 • • 13 – Styles Carousel, User Guide
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:
Code Sample 13-1 — A sample style file
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:
Code Sample 13-2 — Using decimal RGBA values
• • • 132 • • • Carousel, User Guide 13 – Styles
Using System colors System colors can also be used by naming the system color in place of an RGB value, thus
Code Sample 13-3 — Using SystemColors
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:
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.
Table 13-1 — System Colors
• • • • 133 • • 13 – Styles Carousel, User Guide
Name Usage
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.
Table 13-1 — System Colors
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
• • • 134 • • • Carousel, User Guide 13 – Styles
colors to specify the feedback colors for the validation states. The validations styles could thus be specified as:
Code Sample 13-4 — Specifying extended styles
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.
Code Sample 13-5 — Setting right aligned label styles and properties
• • • • 135 • • 13 – Styles Carousel, User Guide
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:
Code Sample 13-6 — Style usage
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 (“Layout” on page 143) details how layouts can be controlled within an application. With dynamic content it is always important to consider how the content will appear. Carousel makes it easy to dynamically modify the content of a component but this generally
• • • 136 • • • Carousel, User Guide 13 – Styles
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 Carousel. 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 Carousel makes this even easier than normal by allowing you to simply plug-in the look and feel via a startup parameter. Carousel 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 XUI and hence you can
• • • • 137 • • 13 – Styles Carousel, User Guide
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
LAF=net.xoetrope.optional.laf.SynthInstaller
Table 13-2 — Setting the look and feel
This setting refers to a helper class that installs the LAF and takes care of any initialization that is necessary. Carousel 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.
SynthConfigFile=demo.xml SynthResourceLoader=mypackage.MyClass
Table 13-3 — Synth parameters in the start-up file
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 XUI 3.0, this application is another demo of XUI, 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. Carousel can convert a referenced SVG into a PNG raster graphic for use in the
• • • 138 • • • Carousel, User Guide 13 – Styles
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.
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/ Convert an SVG image to a PNG. Optional width button.png 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/ Convert an SVG image to a TIFF. Optional width button.tif and height attributes can also be specified after the source image name. If they are omitted a 50x50 image is created.
Table 13-4 — Synth substitution expression
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.
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
Table 13-5 — SVG substitution expression
• • • • 139 • • 13 – Styles Carousel, User Guide
Expression Example Result Usage
${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
Table 13-5 — SVG substitution expression
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. Carousel 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 XUI. 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 Carousel 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:
Table 13-6 — Setting a background painter
• • • 140 • • • Carousel, User Guide 13 – Styles
The example below shows a number of panels using this custom painter to display a gradient background with a watermark logo.
Figure 13-5 — Custom panels and the Synth Look and Feel.
Painters XUI 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. XUI 3.0 adopts 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.
public void paint( Graphics2D g2d, JComponent comp, int w, int h );
Table 13-7 — The painters API
and can be used in a varient of XUI and Carousel 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 Carousel 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
Table 13-8 —
• • • • 141 • • 13 – Styles Carousel, User Guide
Painter Appearance/Role XTitlePainter Extends the XGradientBackground by adding a caption XGradientBackground 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.
XSvgPainter 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.
XImagePainter Paints an image in the client area.
Table 13-8 —
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.
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
Table 13-9 — Look and feel resources
• • • 142 • • • 14 – Layout Carousel, User Guide
14 Layout
By default Carousel 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 Carousel and in Java to both improve the overall appearance and simplify development. Carousel 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 Carousel 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. Carousel 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 Carousel’s own layout managers; the GuideLayout and the ScaleLayout.
• • 143 • • • • 14 – Layout Carousel, User Guide
Alignment tools
Carousel 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:
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
Table 14-1 — Alignment and Justification Tools
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
• • • 144 • • • Carousel, User Guide 14 – Layout
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
Table 14-2 —
Guides
Drawing on the techniques used in desktop publishing and document layout, Carousel 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.
• • • • 145 • • 14 – Layout Carousel, User Guide
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.
Figure 14-1 — Click to the toggle button to enter design mode.
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.
Figure 14-1 — Configuring guides.
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
• • • 146 • • • Carousel, User Guide 14 – Layout
constraint specified for each component on the panel controlled by the guide layout manager.
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
Table 14-3 — Guide options
• • • • 147 • • 14 – Layout Carousel, User Guide
Several more options are available for configuration of the guides:
Figure 14-2 — Setting up row and column 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:
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.
Table 14-4 — Guide configuration options
• • • 148 • • • Carousel, User Guide 14 – Layout
Menu option Function
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.
Table 14-4 — Guide configuration options
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.
Figure 14-3 — Layout hints while positioning components.
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).
• • • • 149 • • 14 – Layout Carousel, User Guide
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 Figure 14-4 — Setting up a set of row and column guides. 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
• • • 150 • • • Carousel, User Guide 14 – Layout
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.
Figure 14-5 — A simple page using a guide layout at its default size.
The XML code for the complete form layout is shown below.
Table 14-5 — Guide layout
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.
• • • • 151 • • 14 – Layout Carousel, User Guide
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:
Table 14-6 — Including guides
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.
Figure 14-6 — The same simple page using a guide layout following resizing.
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 Carousel, 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 Carousel.
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.
Table 14-7 — Layout types
• • • 152 • • • Carousel, User Guide 14 – Layout
LayoutManager Description
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 A XUI 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
Table 14-7 — Layout types
• • • • 153 • • 14 – Layout Carousel, User Guide
Carousel 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 XUI setup. These layout managers include:
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
Table 14-8 — Extra layouts
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 Carousel 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.
• • • 154 • • • Carousel, User Guide 14 – Layout
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 Carousel 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:
Table 14-9 — Sample Page using Layout Managers
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 Kalideoscope editor will present a list of the constraints that are appropriate for the particular layout manager.
Framesets
In earlier chapters (“Frames setup” on page 85) 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.
• • • • 155 • • 14 – Layout Carousel, User Guide
The frameset is control by a border layout (see “Layout managers” on page 110) 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 Carousel. Using the targets is straightforward. We have seen how a page can be displayed by calling the showPage(...) method:
// Via the page manager member variable of the page pageMgr.showPage( “DetailsPage” );
Table 14-10 — Showing a single page
A second version of the showPage method takes an additional argument that specifies the target area:
// The manager objects pageMgr.showPage( “DetailsPage”, “content” ); pageMgr.showPage( “StatusPage”, “footer” );
Table 14-11 — Showing a single page in the content target area
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 Carousel 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
• • • 156 • • • Carousel, User Guide 14 – Layout
content area. To stop the framework loading the home page in this way set the startup property StartClass to NONE:
StartClass=NONE
Table 14-12 — Specifying that no home page should be loaded
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 Carousel 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 “Application styles” on page 195.
References
For more information on layout managers see the Sun’s Java documentation. http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html
• • • • 157 • • 14 – Layout Carousel, User Guide
• • • 158 • • • 15 – Data binding Carousel, User Guide
15 Data binding
Carousel 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.
Figure 15-1 — The Model-View-Controller architecture
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. Carousel 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 Carousel 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.
• • 159 • • • • 15 – Data binding Carousel, User Guide
Carousel also saves its own data to the model, for example saving the component state data to the xui_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.
Figure 15-2 — The hierarchical data model, with composite nodes
Data binding
Carousel 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 Carousel takes care of getting and saving the data via data binding objects which are implicitly constructed as your pages are loaded.
• • • 160 • • • Carousel, User Guide 15 – Data binding
An example binding is shown below.
Code Sample 15-1 — Simple data binding
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 Carousel will automatically create storage for the data. So, if for instance the user interface component is an input component like an edit field Carousel 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.
Figure 15-3 — Using the model to pass data between pages.
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.
• • • • 161 • • 15 – Data binding Carousel, User Guide
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 Carousel get the data in the first place? The data source used by Carousel 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.
ModelData=simpledatasets.xml
Code Sample 15-2 — The data source configuration reference in the startup file
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.
Code Sample 15-3 — The data source configuration file (simpledataset.xml)
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.
• • • 162 • • • Carousel, User Guide 15 – Data binding
The actual data file is also relatively simple. The important attributes to note are the id attribute and the value attribute.
Code Sample 15-4 — The static data file (simpledata.xml)
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 ids 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 Carousel and Carousel 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. Carousel 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 (“Update on page display” on page 166). 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 Carousel 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.
• • • • 163 • • 15 – Data binding Carousel, User Guide
Carousel 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:
Code Sample 15-5 — A simple data binding for an edit field
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 Carousel 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 Carousel 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 Carousel creates a Carousel saves state new node for the data by appending the source path to the ‘xui_state’ node. Thus where the above information beneath example sourced its data from the ‘person/name’ node the result is saved to ‘xui_state/person/ the xui_state node. 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 ‘xui_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 Carousel automatically configures the path by appending the source path to the xui_state node to give an output path of xui_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 xui_state node. The output path can be specified explicitly in the binding or XUI can create it automatically by appending the source path to the path /xui_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.
• • • 164 • • • Carousel, User Guide 15 – Data binding
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:
Code Sample 15-6 — Some more bindings
While Carousel 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:
addBinding( new XTextBinding( firstNameEdit, "person/firstName" )); addBinding( new XTextBinding( secondNameEdit, "person/secondName" ));
Code Sample 15-7 — Adding a binding in Java
In this example we know that the bound user-interface components are edit fields and therefore we use the XTextBinding binding type. Some of the bindings provided by Carousel are:
Binding Usage
XLabelBinding A simple binding that is used for read-only Label components
XTextBinding 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
XListBinding 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.
XStateBinding 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
Table 15-1 — Some data binding types
• • • • 165 • • 15 – Data binding Carousel, User Guide
Happily once the bindings have been constructed there is no difference between bindings constructed via Java or via XML. Carousel 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
Carousel 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 Carousel 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.
• • • 166 • • • Carousel, User Guide 15 – Data binding
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 'xui_state/
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 Carousel there is another approach. Carousel 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:
source="${myMethod(args)}"
Code Sample 15-8 — An embedded callback method
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
source=”users/${getCurrentUser()}/firstName”
Code Sample 15-9 — A callback substituting a path element
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“Evaluated attributes and helpers” on page 273.
• • • • 167 • • 15 – Data binding Carousel, User Guide
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:
Code Sample 15-10 — Some sample table bindings
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.
• • • 168 • • • Carousel, User Guide 15 – Data binding
A new database table can be configured as follows:
DatabaseTableModel completeVoltageTable = DatabaseTableModel.getTable( "Voltages" ); completeVoltageTable.setDistinct( true ); completeVoltageTable.setOrderField( "ID" ); completeVoltageTable.retrieve();
Code Sample 15-11 — Select DISTINCT values from a table
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.
DatabaseTableModel voltageTable = new DatabaseTableModel();
// Set the query elements // FROM clause, FIELDS, WHERE clause voltageTable.setupTable( "CS_VOLTAGES", "VOLTAGE_DESCRIPTION, VOLTAGE_MIN, VOLTAGE_MAX", "FREQUENCY=50" ); voltageTable.retrieve();
Code Sample 15-12 — Setup a database query
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
myTable.setModel( voltageTable ); updateBoundComponentValues();
Code Sample 15-13 — Set and refresh a table control
• • • • 169 • • 15 – Data binding Carousel, User Guide
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.
Figure 15-1 — Linking components via the model.
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.
• • • 170 • • • Carousel, User Guide 15 – Data binding
An example of this is the page described below:
Code Sample 15-14 — A complete example of table usage
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.
• • • • 171 • • 15 – Data binding Carousel, User Guide
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 Carousel model is saved to a specific subpath in the model, the 'xui_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 'xui_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.
Code Sample 15-15 — Example linking of bindings
Finally, Carousel 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
CaseSensitiveDatabase=false
Code Sample 15-16 — Flag case sensitivity
Advanced attribute evaluation and libraries
One of the goals XUI 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 XPage 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 XUI page can be specified dynamically, for example
Table 15-2 — Basic attribute evaluation
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.
• • • 172 • • • Carousel, User Guide 15 – Data binding
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:
public void myMethod();
Table 15-3 — Method signature
In XUI 2.0 The attributes can also be defined in classes other than the current page or classes derived from XPage. The syntax for such expressions is as follows:
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.
Table 15-4 — Extended attribute declarations
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.
• • • • 173 • • 15 – Data binding Carousel, User Guide
What this means in practice is that the class or classes implementing an applications business logic no longer need be derived from XPage. In this way it is possible to build libraries of reusable functions. Lets look at an example:
1 target="IncBtn" type="ActionHandler"/> 15
Table 15-5 — Sample extended attributes
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 XUI 2.0 syntax (which is still valid) except that the handler method is now in a separate class (com.xoetrope.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.
• • • 174 • • • Carousel, User Guide 15 – Data binding
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.
Adding new binding types
The data binding mechanism in Carousel shares some common features with the other configuration file systems in Carousel and XUI. 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 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 XRegisteredDataBindingFactory via the addConfigFile method:
String filePath = project.findResourceAsString( “data_bindings.xml” ); XRegisteredDataBindingFactory.addConfigFile( “myModuleName”, filePath, false );
Table 15-6 — Add a data binding configuration file
The configuration file format
The structure of a data binding
Data binding adapter
Data binding lifecycle
Data binding contexts and containers TODO Describe new data binding configuration file format/mechanism: bindings.xml
• • • • 175 • • 15 – Data binding Carousel, User Guide
TODO Creating a new binding type, the role, the responsibility
Using the XModelHelper
• • • 176 • • • 16 – Event handling Carousel, User Guide
16 Event handling
Carousel 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 Carousel the most common instances of this event handling wiring is taken care of by basic library functions so that all the Carousel user need do is specify what is specific to a particular event. Carousel’s event handling relies on the basic Java event handling mechanisms and at any point you can go beyond the Carousel library and work directly with the low level event mechanisms. This chapter shows how to work with both mechanisms. TODO Describe new event handling configuration file format/mechanism TODO Describe the new event registration/processing mechanism TODO Describe new scripting support TODO Describe new evaluation mechanism TODO Describe library methods in more detail
Adding EventHandlers interactively
Adding event handling in Carousel 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 Carousel 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 Carousel 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
• • 177 • • • • 16 – Event handling Carousel, User Guide
name has been given to the component being edited then a popup dialog will appear requesting that you enter a name for the component.
Figure 16-1 — Enter the name of an event handler Once a name is available for the component Carousel can proceed and opens the source code in a text editor at the new event handler method.
Figure 16-2 — View the Java source code for an event handler.
• • • 178 • • • Carousel, User Guide 16 – Event handling
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..
Figure 16-3 — View the event handler declarations in XML.
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.
• • • • 179 • • 16 – Event handling Carousel, User Guide
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 XPage 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 XPage so that calls specified in the XML can be intercepted by the custom class.
public class EventHandling extends XPage
Code Sample 16-1 — A new Java class declaration
The XML file now specifies this custom class as it's base class and defines the two buttons which will have events attached.
Code Sample 16-2 — A page specifying events
So now the java code handling the events is as follows:
public void nextPage() { if ( wasMouseClicked() ){ // Do something here... XPageManager.showPage( getAttribute( "next" ) ); } }
public void cancel() { if ( wasMouseClicked() ){ XButton button = ( XButton ) findComponent( "btnCancel" ); button.setText( "Clicked!" ); } }
Code Sample 16-3 — The Java class’s implementation of the events
• • • 180 • • • Carousel, User Guide 16 – Event handling
Built-in event handlers
Carousel supports several basic types event, the support types are described below:
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.
Table 16-1 — Various event handler types
All of these basic event handlers are added via the XPage 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:
addMouseHandler( btnProceed, "nextPage" ); addMouseHandler( btnCancel, "cancel" );
Code Sample 16-4 — Adding an event handler with Java
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
• • • • 181 • • 16 – Event handling Carousel, User Guide
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.
public void handleMouseClicks() { if ( wasMouseClicked()) { MouseEvent me = (MouseEvent)getCurrentEvent(); Point pt = me.getPoint(); statusLabel.setText( “The mouse is at: + Integer.toString(pt.x) + “,” + Integer.toString(pt.y)); } }
Code Sample 16-5 — Accessing the current event
Under the hood
In adding event handlers Carousel has to go through a number of steps and it is worth understanding these steps, if only for the sake of in-depth knowledge.
Figure 16-4 — Invoking an event handler via the XPage’s listeners.
Consider first of all the process of adding the event handler. Carousel 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 XPage which
• • • 182 • • • Carousel, User Guide 16 – Event handling
includes the event handling mechanism. In processing the event declaration in your page’s XML Carousel therefore uses the page’s event handling mechanism. Like most normal Java code Carousel 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 Carousel 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 Carousel 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 XUI application. The XEventAdapter 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 XUI event handler.
Table 16-2 — Adding a custom event handler
The XChangeEventHelper 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 XEventAdapter interface, which specifies the parameters needed to create and add the event handler. The complete source for the above helper is shown below
package net.xoetrope.optional.events.swing;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.xoetrope.xui.events.XEventAdapter; import net.xoetrope.xui.events.XuiEventHandler;
/** * A helper for the Swing ChangeListener interface *
Copyright (c) Xoetrope Ltd., 2002-2005
*License: see license.txt
* $Revision: 1.2 $ */ public class XChangeEventHelper implements ChangeListener, XEventAdapter { protected XuiEventHandler eventHandler;Table 16-3 — XChangeEventHelper, a custom event handler
• • • • 183 • • 16 – Event handling Carousel, User Guide
/** * Set the current event handler * @param xeh the event handler */ public void setEventHandler( XuiEventHandler xeh ) { eventHandler = xeh; }
/** * Get the name of the adder method e.g. addActionListener * @return the method name */ public String getAddedMethodName() { return "addChangeListener"; }
/** * Get the name of the listener e.g. java.awt.event.ActionListener * @return the listener name */ public String getListenerInterfaceName() { return "javax.swing.event.ChangeListener"; }
/** * Get the event mask * @return the mask e.g. AWTEvent.ACTION_MASK */ public long getEventMask() { return 0x100000; }
/** * The change event has occured */ public void stateChanged( ChangeEvent e ) { try { eventHandler.invoke( getEventMask(), e ); } catch ( Exception ex ) { ex.printStackTrace(); } } }
Table 16-3 — XChangeEventHelper, a custom event handler
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 Carousel 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
• • • 184 • • • Carousel, User Guide 16 – Event handling
with a certain degree of separation from the normal flow of events since, as the name suggests they occur in exceptional circumstances.
Figure 16-5 — Plug-in a custom exception handler to redirect error messages.
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. Carousel 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 “Writing a custom exception handler” on page 206
Generic event handling
Carousel 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 Carousel 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 XPage class. In subclassing XPage 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 Carousel. 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.
• • • • 185 • • 16 – Event handling Carousel, User Guide
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 XUI should interfere with the basic event handling mechanism.
public void setEventHandler( XEventHandler eh );
public void addListener( Object comp, String listenerName, String argType ); public void addHandler( Object comp, long eventType, String methodName ) throws Exception;
Code Sample 16-6 — Event Handler API
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 as addActionHandler, 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 XPage 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:
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[referenceN for a named object instance ame].myMethod
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.
Table 16-4 — Event specifications
• • • 186 • • • Carousel, User Guide 16 – Event handling
To illustrate this extended event specification syntax let’s have a look at an example.
1
Table 16-5 — Counter example extended with library functions
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 com.xoetrope.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 XPage. 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.
• • • • 187 • • 16 – Event handling Carousel, User Guide
• • • 188 • • • 17 – Navigation and page management Carousel, User Guide
17 Navigation and page management
Carousel 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 Carousel 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 Carousel each page can be regarded as a discrete entity displayed by the application or applet. All pages must be derived from the basic XPage class and this class interacts with the XPageManager 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 XPageManager for the current project the call below can be used.
XProjectManager.getPageManager();
Code Sample 17-1 — Retrieving the XPageManager for the current project
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.
Code Sample 17-2 — Setting up the navigation button and response event
• • 189 • • • • 17 – Navigation and page management Carousel, User Guide
The page must then implement the proceed response method. The XPage class contains a reference to the XPageManager for the current XProject and can be used directly from subclassed XPages as shown below:
package myPackage;
import net.xoetrope.xui.*; import net.xoetrope.swing.*; import net.xoetrope.xui.data.*; import net.xoetrope.xui.helper.*;
public class MyCustomPageClass extends XPage {
public MyCustomPageClass() { }
...
public void proceed() { pageMgr.showPage( "Page2"); } }
Code Sample 17-3 — Implementing the response method
The page manager
The Carousel 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 XPage’s methods are used to display pages and this is the case for the normal response methods used in Carousel 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
• • • 190 • • • Carousel, User Guide 17 – Navigation and page management
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 Carousel can include URLs in this way. Targets Carousel supports the notion of framesets similar to those used in HTML. Like HTML the framesets in Carousel 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” on page 155) Page history In addition to the showPage method Carousel 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.
• • • • 191 • • 17 – Navigation and page management Carousel, User Guide
To reset the page cache in this way use the following code:
// Use the XPage’s member variable pageMgr! pageMgr.reset();
Code Sample 17-4 — Reset the page cache
Page loaders Carousel 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 XPageLoader interface and can be configured through the startup property value for BuilderClass
Code Sample 17-5 —
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 “Data management” on page 251. Once loaded, the application will have access to these definitions via the XModel 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.
public class NavigationManager {
XBaseModel navMdl; protected int currentNode = 0; BasePage currentPage; private static NavigationManager navMgr;
private NavigationManager( String navPath ) { navMdl = ( XBaseModel ) XProjectManager.getModel().get( “navigation/wizardpages” ); }
Code Sample 17-6 — NavigationManager Singleton class
• • • 192 • • • Carousel, User Guide 17 – Navigation and page management
public static NavigationManager getInstance() { if ( navMgr == null ) navMgr = new NavigationManager()
return navMgr; }
public static void next() { getInstance.showNextPage(); }
public static void prev() { getInstance.showPrevPage(); }
public void showPrevPage() { currentNode--; showpage();
}
public void showNextPage() { if ( currentPage == null ) { XBaseModel mdl = (XBaseModel) navMdl.get( currentNode ); String pagename = (String) mdl.get(); currentPage = (BasePage) XProjectManager.getPageManager().getPage( pagename ); } if ( currentPage.checkValidations() == 0 ) { currentNode++; showpage(); } }
public void showHomePage() { currentPage = null; currentNode = 0; XBaseModel mdl = (XBaseModel) navMdl.get( currentNode ); String pagename = (String) mdl.get(); currentPage = (BasePage) XProjectManager.getPageManager().showPage( pagename ); }
private void showpage() { XBaseModel mdl = ( XBaseModel ) navMdl.get( currentNode ); String pagename = ( String ) mdl.get(); currentPage = ( BasePage ) XProjectManager.getPageManager().showPage( pagename ); }
}
Code Sample 17-6 — NavigationManager Singleton class
• • • • 193 • • 17 – Navigation and page management Carousel, User Guide
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.
Code Sample 17-7 — A navigation page XML declaration
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.
// In startup.properties specifc the Lifecycle object // LifeCycleListener=com.xoetrope.beaumont.ProjectListener
// In that object map the "START_PAGE" page name public class ProjectListener implements XLifeCycleListener {
/** * Called when the application/applet has been created and initialized. * @param project the owner project */ public void initialize( XProject p ) { String[] args = (String[])project.getObject( "StartupArgs" ); // Set the start page to the 3rd command-line parameter p.getPageManager().mapPageName( "START_PAGE", args[ 3 ] ); } }
Table 17-1 — Setup the application lifecycle listener to set the initial page name
and instead of hardcoding a page name in the frames file it can be replaced with the appropriate key:
// In the frames.xml include the "START_PAGE" key
Table 17-2 — Set the content using a key instead of a page name
• • • 194 • • • 18 – Application styles Carousel, User Guide
18 Application styles
Carousel 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.
• • 195 • • • • 18 – Application styles Carousel, User Guide
Application styles
XUI supports multiple application styles as metntioned above. These styles are:
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.
Table 18-1 — Application styles
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.
Internal Frames
TODO handler.
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.
• • • 196 • • • Carousel, User Guide 18 – Application styles
Using the docking layout As mentioned above, to use the docking layout you just need to select the appropriate startup application net.xoetrope.swing.docking.XDockingApp
java -cp XuiCoreSwing.jar;XuiOptional.jar net.xoetrope.swing.docking.XDockingApp
Table 18-2 — Startup for the docking application style
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
Figure 18-1 — A sample docking layout.
Setting the layout configuration The basic frameset is specified using the frames file that was used for the basic frames setup earlier (see “Framesets” on page 155), but for docking layouts we must add some extra information detailing how the screen is to be divided up. For example the following:
Table 18-3 — Setting up a docking layout
• • • • 197 • • Carousel, User Guide 18 – Application styles
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:
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.
Table 18-4 — Docking frame attributes
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 Carousel and XUI applications can be embedded in a standard Swing application as though the Carousel application was just a Panel or just another component being used by the application. The key to embedding a Carousel 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 Carousel application. TODO: insert code examples, explain the startup process
Modular application
Just as a Carousel application can be embedded in a legacy application, one Carousel application can be embedded in another. In fact multiple Carousel 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.
• • • • 198 • • Carousel, User Guide 18 – Application styles
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. Carousel’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. TODO examples, setup and loading, inter module communication, data separation, security, other issues Set startup objects Multple startup objects can be added simply by adding startup property entries in the form:
StartupObject
Table 18-5 — Listing the startup objects
where
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.
Figure 18-2 — System tray support.
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
• • • • 199 • • 18 – Application styles Carousel, User Guide
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 XUI application. To use this support the application must add an instance of the net.xoetrope.swing.deploy.XSystemTrayManager class. A good place to do this is in a XLifeCycleListener implementation
public class ProjectListener implements XLifeCycleListener { private static XSystemTrayManager sysTray; /** * Called when the application/applet has been created and initialized. * @param project the owner project */ public void initialize( XProject proj ) { final XProject project = proj; if ( sysTray == null ) { sysTray = XSystemTrayManager.getInstance( project );
// Do remaining initialization and network access } }
/** * Called when the application/applet has been shutdown and is about to exit */ public void shutdown() { } }
Table 18-6 — Setting up the system tray
Embedded webserver A minimal http server is now embedded at net.xoetrope.optional.http.XHttpServer. 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 TODO clear up this explanation and an example to the public code base. 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 XSystemTrayManager takes care of most of the work and it will invoked the XActivation 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:
project.setObject( "ActivationObject", this );
Table 18-7 — Setting the activation object
• • • 200 • • • Carousel, User Guide 18 – Application styles
Typically the application will restart its user interface once reactivated. Of course care should be taken to do this on the EventDispatchThread.
public void activate() { String[] args = (String[])project.getObject( "StartupArgs" ); final String eventType = args.length > ProjectListener.EVENT_TYPE_ARG ? args[ ProjectListener.EVENT_TYPE_ARG ] : null; final String eventID = args.length > ProjectListener.EVENT_TYPE_ID ? args[ ProjectListener.EVENT_TYPE_ID ] : null;
// Query the startup event in the background if (( eventID != null ) && ( eventID.length() > 0 )) { SwingWorker worker = new SwingWorker() { public Object construct() { // Do the work off the EDT String url = "workspaces/myevent/" + eventID;
ProjectListener.queryEvents( project, eventType, url ); return null; }
public void finished() { // Now, on the EDT update the tree and show the event. getBinding( taskList ).get(); showEvent( eventID, eventType ); setSelectedNode( eventID ); } }; worker.start(); } }
Table 18-8 — A typical restart handler2
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
/** * Initialize the project's connection */ public class ProjectListener implements XLifeCycleListener { ...
/** * Called when the application/applet has been shutdown and is about to exit */ public void shutdown() { }
Table 18-9 — Prevent JVM Exit
• • • • 201 • • 18 – Application styles Carousel, User Guide
• • • 202 • • • 19 – Input validation Carousel, User Guide
19 Input validation
Carousel 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.
TODO Describe new data validation file format/mechanism
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.
Code Sample 19-1 — A simple validation file
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
• • 203 • • • • 19 – Input validation Carousel, User Guide
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.
Validations=validations.xml
Code Sample 19-2 — New line in the startup.properties
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.
Code Sample 19-3 — The validated 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 ‘net.xoetrope.test.Personal’. For now, simply insert the following function into the class:
public void doValidation() { int ret = checkValidations(); }
Code Sample 19-4 — Prepare to handle validations with an event handler function
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 net.xoetrope.xui.validation.XValidator interface. This code works but it is not a particularly elegant way of handling the validations and the section “Writing a custom exception handler” on page 206, will also look at how to handle and display the validations in a more programmer friendly way.
• • • 204 • • • Carousel, User Guide 19 – Input validation
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
Code Sample 19-5 — An in-line validation
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.
Code Sample 19-6 —
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
TODO add a description and example code Carousel 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.
• • • • 205 • • 19 – Input validation Carousel, User Guide
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. TODO add example code
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. TODO add example of programmatic triggering of validation
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 XExceptionHandler interface is created. The XExceptionHandler interface contains two methods.
public boolean handleException( Object c, Exception ex, Object checker );
Code Sample 19-7 — The handleException method definition
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.
public int accumulateMessages( boolean accumulate, int level );
Code Sample 19-8 — The accumulateMessages method definition
• • • 206 • • • Carousel, User Guide 19 – Input validation
Another method is now required, the accumulateMessages method is called twice when the XPage.checkValidations 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.
package net.xoetrope.test;
import java.util.*; import java.awt.*; import net.xoetrope.xui.*; import net.xoetrope.xui.exception.*; import net.xoetrope.xui.validation.*;
public class ExceptionHandler implements XExceptionHandler { boolean pageValidation = false; private XPage currentPage; Vector errors, warnings;
public ExceptionHandler( XPage page ) { currentPage = page; }
public boolean handleException( Object comp, Exception ex, Object xvalidator ) { XValidator validator = ( XValidator ) xvalidator; if ( ( validator.getLevel() == validator.LEVEL_ERROR ) && ( ! pageValidation ) ){ currentPage.showMessage( "Input error", ex.getMessage() ); return true; } String msg = validator.getMessage(); if ( validator.getLevel() == validator.LEVEL_ERROR ) errors.add( msg ); else warnings.add( msg );
return true; }
public int accumulateMessages( boolean start, int level ) { pageValidation = start; if ( pageValidation ){ errors = new Vector(); warnings = new Vector(); } else { if ( errors.size() > 0 || warnings.size() > 0 ) { Toolkit.getDefaultToolkit().beep(); currentPage.showMessage( "Error", formatMessages() ); } } return level; }
Code Sample 19-9 — The entire ExceptionHandler class
• • • • 207 • • 19 – Input validation Carousel, User Guide
private String formatMessages() { String msg = ""; for ( int i = 0; i < errors.size(); i++ ) msg += "error :" + errors.elementAt( i ) + "\n"; for ( int i = 0; i < warnings.size(); i++ ) msg += "warning :" + warnings.elementAt( i ) + "\n"; return msg; } }
Code Sample 19-9 — The entire ExceptionHandler class
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.
public ExceptionHandler( XPage page ) { currentPage = page; }
Code Sample 19-10 — The ExceptionHandler constructor
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.
public boolean handleException( Object comp, Exception ex, Object xvalidator ) { XValidator validator = ( XValidator ) xvalidator; if ( ( validator.getLevel() == validator.LEVEL_ERROR ) && ( ! pageValidation ) ){ currentPage.showMessage( "Input error", ex.getMessage() ); return true; } String msg = validator.getMessage(); if ( validator.getLevel() == validator.LEVEL_ERROR ) errors.add( msg ); else warnings.add( msg );
return true; }
Code Sample 19-11 — Implementing the handleException method
• • • 208 • • • Carousel, User Guide 19 – Input validation
The accumulateMessages method is shown below
public int accumulateMessages( boolean start, int level ) { pageValidation = start; if ( pageValidation ){ errors = new Vector(); warnings = new Vector(); } else { if ( errors.size() > 0 || warnings.size() > 0 ) { Toolkit.getDefaultToolkit().beep(); currentPage.showMessage( "Error", formatMessages() ); } } return level; }
Code Sample 19-12 — Implementing the accumulateMessages method
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 XValidationFactory 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.
• • • • 209 • • 19 – Input validation Carousel, User Guide
Start by creating the class ValidationFactory which extends the XValidationFactory class.
package net.xoetrope.test;
import java.awt.*;
import net.xoetrope.debug.*; import net.xoetrope.xml.*; import net.xoetrope.xui.build.*; import net.xoetrope.xui.validation.*;
public class ValidationFactory extends XValidationFactory {
public XValidator getValidation( String validationName, Method m, int mask, Object page ) { return super.getValidation( validationName, mask, page ); }
}
Code Sample 19-13 — The custom XValidationFactory
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
ValidationFactory=net.xoetrope.test.ValidationFactory
Code Sample 19-14 — The startup.properties line
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 XBaseValidator 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.
package net.xoetrope.test;
import java.awt.*;
import net.xoetrope.xui.validation.*;
public class NumberValidation extends XBaseValidator {
public void validate(Object c, boolean forceMandatory) throws Exception { String text = getText(c); if (text.trim().length() > 0) { try { Double.parseDouble(text); } catch (Exception ex) { errorLevel = LEVEL_ERROR; throwException(); } } }
Code Sample 19-15 — The NumberValidation class
• • • 210 • • • Carousel, User Guide 19 – Input validation
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.
public XValidator getValidation( String validationName, Method m, int mask, Object page ) { XmlElement ele = ( XmlElement ) validations.get( validationName ); String type = ele.getAttribute( "type" );
if ( type.compareTo( "number" ) == 0 ) { NumberValidation validator = new NumberValidation(); validator.setName( validationName ); validator.setMask( mask ); validator.setup( ele ); return validator; } else { return super.getValidation( validationName, mask, page ); } }
Code Sample 19-16 — The modified getValidation method
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’
Code Sample 19-17 — Declaring a new number validation
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
Code Sample 19-18 —
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.
public void validate(Object c, boolean forceMandatory) throws Exception { if (forceMandatory) { String text = getText(c); if (text.trim().length() > 0) { try { Double.parseDouble(text); } catch (Exception ex) { errorLevel = LEVEL_ERROR; throwException(); } } } }
Code Sample 19-19 —
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.
• • • • 211 • • 19 – Input validation Carousel, User Guide
Finally the new validation class is changed to handle the mandatory attribute. This is achieved by overloading the setup method of the XBaseValidator class as mentioned earlier.
public void setup( XmlElement ele ) { String value = ele.getAttribute( "mandatory" ); mandatory = value.compareTo( "true" ) == 0 ? true : false; super.setup( ele ); }
Code Sample 19-20 — The overloaded setup class
This is wherethe attributes defined in the validations.xml file can be used. Set the mandatory attribute to true and run the application again.
public void validate(Component c, boolean forceMandatory) throws Exception { if (forceMandatory) { String text = getText(c); if (text.trim().length() > 0) { try { Double.parseDouble(text); } catch (Exception ex) { errorLevel = LEVEL_ERROR; throwException(); } } else if ( mandatory ){ errorLevel = LEVEL_ERROR; throwException(); } } }
Code Sample 19-21 — Handling the mandatory flag
Simply throw the exception if the mandatory flag is true and the text is zero length.
Getting values from the XPage
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
Code Sample 19-22 — Modifiied age validation
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.
public String getAge() { System.out.println( “In the getAge() function” ); XEdit ageText = ( XEdit )findComponent( "ageText" ); return ageText.getText(); }
Code Sample 19-23 — Create the getAge method
• • • 212 • • • Carousel, User Guide 19 – Input validation
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 XBaseValidator class
package net.xoetrope.test;
import java.awt.*;
import net.xoetrope.xml.*; import net.xoetrope.xui.validation.*;
public class FunctionValidation extends XBaseValidator {
public void validate(Object c, boolean forceMandatory) throws Exception { Integer ret = ( Integer ) invokeMethod(); if ( ret.intValue() > LEVEL_IGNORE ) { errorLevel = ret.intValue(); throwException(); } }
public void setup( XmlElement element ) { message = element.getAttribute( "msg" ); super.setup( element ); }
}
Code Sample 19-24 — The new FunctionValidation
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
Code Sample 19-25 — The agefunction validation declared
• • • • 213 • • 19 – Input validation Carousel, User Guide
Now the custom validation factory needs to be amended to construct the new validation. The getValidation function should now look like this
public XValidator getValidation( String validationName, Method m, int mask, Object page ) { XmlElement ele = ( XmlElement ) validations.get( validationName ); String type = ele.getAttribute( "type" );
if ( type.compareTo( "number" ) == 0 ) { NumberValidation validator = new NumberValidation(); validator.setName( validationName ); validator.setMask( mask ); validator.setup( ele ); return validator; } else if ( type.compareTo( "function" ) == 0 ) { FunctionValidation validator = new FunctionValidation(); validator.setName( validationName ); validator.setup( ele ); return validator; } else { return super.getValidation( validationName, mask, page ); } }
Code Sample 19-26 — The modified getValidation function
Reopen the personal.xml file and insert the following validation.
Code Sample 19-27 — Implementation of the function validation
The method being called in this case is the ‘doAgeValidation’ method so that function needs to be defined within the page.
public Integer doAgeValidation() { return new Integer( XValidator.LEVEL_ERROR ); }
Code Sample 19-28 — The doAgeValidation function
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.
• • • 214 • • • Carousel, User Guide 19 – Input validation
The normal, warning and failure colors used for these validations can be set using the XBaseValidator.setValidationColors, however the validations can also be set using an extended style reference in the validations file
Code Sample 19-29 — Add a style parameter to the validations file
The style attributes are then specified in the styles file as normal:
Code Sample 19-30 — Add the styles to the styles file
• • • • 215 • • 19 – Input validation Carousel, User Guide
• • • 216 • • • 20 – Menus and toolbars Carousel, User Guide
20 Menus and toolbars
Some applications require menus for common operations like File, Save, Print and so on. Although rarely used, Carousel 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.
Code Sample 20-1 — Creating a simple menu
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.
• • 217 • • • • 20 – Menus and toolbars Carousel, User Guide
Hooking up Events
Menu events can be hooked up to response handlers using the same event handling mechanism as used by any other component.
Code Sample 20-2 — Menu Event Handling
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 XPage class. Instead the root menu can be obtained from the XApplet for the current project.
XProjectManager.getCurrentProject().getApplet().getMenuBar();
Code Sample 20-3 — Retrieving the application menu
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
• • • 218 • • • Carousel, User Guide 20 – Menus and toolbars
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.
public class SomePage extends XPage {
String[] name = { "mnuFile", "mnuFileNew", "mnuFileSave" }; String[] tran = { "MNU_FILE", "MNU_NEW_PROJ", "MNU_FILE_SAVE" };
public void changeLanguage() { JMenuBar mBar = project.getApplet().getMenuBar(); for ( int i = 0; i < mBar.getMenuCount(); i++ ) { iterateMenus( mBar.getMenu( i ) ); } }
private void iterateMenus( JMenu menu ) { setMenuText( menu ); for ( int i = 0; i < menu.getMenuComponentCount(); i++ ) { setMenuText( menu.getMenuComponent( i ) ); } }
private void setMenuText( Component c ) { if ( c instanceof XMenu ) { ( ( XMenu ) c ).setText( getText( ( XMenu ) c ) ); } }
private String getText( XMenu menu ) { String name = menu.getName(); for ( int i = 0; i < names.length; i++ ) { if ( name.compareTo( names[ i ] ) == 0 ) { return translate( translations [ i ] ); } } return menu.getText(); }
}
Code Sample 20-4 — Localizing a menu 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.
• • • • 219 • • 20 – Menus and toolbars Carousel, User Guide
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.
Figure 20-1 — Adding an event handler to the menu via the menu editor Using multiple menus [since XUI 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 net.xoetrope.XApplet.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.
• • • 220 • • • Carousel, User Guide 20 – Menus and toolbars
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.
Figure 20-2 — Toolbar items with text and icons
The frameset can contain any number of target areas plus a toolbar element:
Table 20-1 — Setting up a toolbar in the frames.xml file
The toolbar itself is specified as any other page in your application. For the example shown above the toolbar.xml file contains the following:
Table 20-2 — toolbar.xml
• • • • 221 • • 20 – Menus and toolbars Carousel, User Guide
Table 20-2 — toolbar.xml
• • • 222 • • • 21 – Dialogs Carousel, User Guide
21 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. Carousel 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 | Xui | Xui Dialog. Alternatively you can right click on the pages folder in the file view and choose New XUI Page... from the popup menu.
Figure 21-1 — Add a new dialog. When the dialog is displayed enter the name of the new dialog and choose XDialog from the Base class list.
Structure
In Carousel a dialog is special case of a page, the XDialog class is derived from XPage 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 XDialog class is not itself the dialog object from the point of view of the underlying Java system, instead the XDialog is a container nested inside a dialog class or window. The XDialog 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.
• • 223 • • • • 21 – Dialogs Carousel, User Guide
Display
Just as a page is shown with the showPage method a dialog can be called with the showDialog method:
pageMgr.showDialog( "myPopupDialog", translate( "MYDIALOG_TITLE" ), -1, -1 );
Table 21-1 — Showing a dialog
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.
try { XDialog popupDialog = ( XDialog )pageMgr.loadPage( "myPopupDialog” );
Point pt = (( MouseEvent )getCurrentEvent() ).getPoint(); Point topLeft = myObject.getLocationOnScreen(); pt.translate( topLeft.x, topLeft.y ); popupDialog.setLocation( pt ); popupDialog.setModal( true ); popupDialog.pack(); popupDialog.showDialog( this ); } catch ( Exception e ) { e.printStackTrace(); }
Code Sample 21-1 — Displaying a dialog
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 A dialog page must be calling method is blocked till the dialog is dismissed. A dialog can be dismissed or closed by an instance of class calling the closeDlg or cancelDlg methods. XDialog or a class For a modeless dialog it is sometimes useful to know when the dialog is dismissed even if the derived from XDialog. 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. Carousel 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
• • • 224 • • • Carousel, User Guide 21 – Dialogs
the edges of the dialog and so that the components are not butted up against the edge of the dialog.
Code Sample 21-2 — Padding a dialog
The above example shows the declaration of a dialog in XML as mentioned above in “Structure” on page 223. It is important to note that a dialog must be of class XDialog or a class derived from the XDialog class (in either the net.xoetrope.swing.XDialog or net.xoetrope.awt.XDialog variant). The XDialog 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.
popupDialog.setModal( true );
Code Sample 21-3 — Making a dialog modal
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:
XDialog popupDialog = ( XDialog )pageMgr.loadPage( "MyPopup" );
popupDialog.setModal( true ); popupDialog.pack(); popupDialog.showDialog( this, "My Dialog", new Point( 100, 100 ) );
Code Sample 21-4 — Specifying the dialog location explicitly
• • • • 225 • • Carousel, User Guide 21 – Dialogs
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:
XDialog popupDialog = ( XDialog )pageMgr.loadPage( "MyPopup" );
popupDialog.setModal( false ); popupDialog.pack(); popupDialog.showDialog( this, “My Dialog”, null ); XuiUtilities.centerOnScreen( popupDialog );
Code Sample 21-5 — Centering a modeless dialog
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.
XSwingHelper.showDialog( this, "MyPopup", "My Title", true, new Point( 100, 100 ));
Code Sample 21-6 — Setting the dialog title
Helpers The dialog can also be displayed with the helper XSwingHelper, or the AWT version XAwtHelper.
Code Sample 21-7 — Using XSwingHelper to show a modal dialog.
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.
getEventHandler().suppressFocusEvents( true );
Code Sample 21-8 — Turning off focus event handling
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
• • • • 226 • • Carousel, User Guide 21 – Dialogs
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:
Code Sample 21-9 — The calling page declaration
And the popup dialog which reproduces some of the input is as follows:
Code Sample 21-10 — The dialog page declaration
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.
public void openDialog() { saveBoundComponentValues(); XDialog dlg = (XDialog)pageMgr.loadPage( "CustDlg" ); dlg.pack(); dlg.showDialog( this ); }
Code Sample 21-11 — The openDialog function
• • • • 227 • • 21 – Dialogs Carousel, User Guide
The first line of this function makes a call to the saveBoundComponentValues function of the XPage 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 XDialog class follows.
public class CustDlg extends XDialog { private boolean okClicked = false;
public void pageActivated() { okClicked = false; }
public void okClick() { okClicked = true; closeDlg(); }
public void cancelClick() { closeDlg(); }
public void saveBoundComponentValues() { if ( okClicked ) super.saveBoundComponentValues(); }
}
Code Sample 21-12 — The CustDlg class
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.
• • • 228 • • • Carousel, User Guide 21 – Dialogs
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:
Code Sample 21-13 — The dialog page with the calls to closeDlg and cancelDlg
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.
• • • • 229 • • 21 – Dialogs Carousel, User Guide
• • • 230 • • • 22 – Localization Carousel, User Guide
22 Localization
Carousel 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
Carousel 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. Carousel 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 XPage 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.
• • 231 • • • • 22 – Localization Carousel, User Guide
Setting the resource attribute for a page
In order to show the mechanisms involved in setting the language for an XPage it would be useful to show an example.
Code Sample 22-1 — A basic page using a resource
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.
GREETING=Hello BYE=Goodbye
Code Sample 22-2 — The en.properties file
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.
Code Sample 22-3 — Set the resource attribute to blank
The default language can now be set in the startup.properties file with the following entry.
Language=en
Code Sample 22-4 — Setting the default application language
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.
GREETING=Bonjour BYE=Au Revoir
Code Sample 22-5 — The fr.properties file
Set the ‘Language’ startup property to ‘fr’ and the first page will read ‘Bonjour’.
• • • 232 • • • Carousel, User Guide 22 – Localization
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.
Code Sample 22-6 — The modified Welcome page
The two push buttons have ActionHandler events associated with them so the ‘com.xoetrope.langsample.Welcome’ class needs to be created to implement these handlers, we can see this below:.
package com.xoetrope.langsample.Welcome;
import net.xoetrope.xui.*; import net.xoetrope.langmgr.LanguageChooserDialog
public class PosScreen extends XPage {
public void changeLang() { saveBoundComponentValues();
LanguageChooserDialog chooser = new LanguageChooserDialog( project.getStartupParam( "Language" )); chooser.pack(); chooser.showDialog( this, null ); String language = chooser.getSelectedLanguage(); if ( language != null ) { // Set the startup parameter for subsequent pages. project.setStartupParam( "Language", "en" );
//Unload all of the cached pages pageMgr.reset();
// reload the current page or reset/reinitialize the page if necessary. pageMgr.showPage( "Welcome" );
updateBoundComponentValues(); } }
public void next() { pageMgr.showPage("NextScreen"); } }
Code Sample 22-7 — The implementing class for the Welcome page
• • • • 233 • • Carousel, User Guide 22 – Localization
The changeLang method sets the new language, then clears the cached pages in the XPageManager 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.
Code Sample 22-8 — The NextScreen XML definition
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: • CAROUSEL_SELECT_LANGUAGE, • CAROUSEL_CANCEL, • CAROUSEL_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 Carousel] Carousel includes an editor (“The language editor” on page 237) 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 Carousel’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.
• • • • 234 • • Carousel, User Guide 22 – Localization
Distribution of language files
Carousel’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 Carousel the language strings are stored in properties files. These files must be encoded in an encoding that matches your applications encoding. Carousel 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
currentProject.setResourceBundleLoader( new EncodedLanguageResourceBundleLoader( currentProject ));
Table 22-1 — Setup the resource bundle loader2
Then, for each language a .property_encodings file can be created, so for example a language XX can have the following additional properties.
encoding=UTF16 font_arial=NimSum font_arial10=NimSum;12
Table 22-2 — XX.property_encodings
• • • • 235 • • 22 – Localization Carousel, User Guide
• • • 236 • • • 23 – The language editor Carousel, User Guide
23 The language editor
[This is a feature of Carousel]
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 Carousel environment.
• • 237 • • • • 23 – The language editor Carousel, User Guide
Elements of the language editor
Below is a screenshot showing the language editor
Figure 23-1 — The language editor.
The key elements of this editor are:
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.
Table 23-1 — The language editor’s key elements
• • • 238 • • • Carousel, User Guide 23 – The language editor
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.
Table 23-1 — The language editor’s key elements
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.
• • • • 239 • • Carousel, User Guide 23 – The language editor
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.
Figure 23-2 — The spell checker.
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
• • • • 240 • • Carousel, User Guide 23 – The language editor
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.
Figure 23-3 — The language database connection setup.
The dialog allows you to enter the settings that control the driver and the connection.
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.
Table 23-2 — Database parameters
• • • • 241 • • 23 – The language editor Carousel, User Guide
Parameter Usage
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
Table 23-2 — Database parameters
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.
Figure 23-4 — Open language files. 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
• • • 242 • • • Carousel, User Guide 23 – The language editor
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]
• • • • 243 • • 23 – The language editor Carousel, User Guide
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 7. Choose the File|New... menu option 8. 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.
Figure 23-1 — Open a file.
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.
Figure 23-2 — Add a language
• • • 244 • • • Carousel, User Guide 23 – The language editor
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 Xoetrope'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
Will the real ¦slim_shady¦ please stand up
Table 23-3 — A simple substitution in its raw form
• • • • 245 • • 23 – The language editor Carousel, User Guide
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
Will the real Marshall Mathers please stand up.
Table 23-4 — The processed string, complete with substitution.
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.
• • • 246 • • • Carousel, User Guide 23 – The language editor
Database table formats The database tables are described below using the SQL CREATE TABLE statement.
CS_LANGUAGES
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]
CS_TRANSLATION
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]
Table 23-5 — Database language table setup
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.
4 0|Reference|Reference 1|Hints|Hints 2|EN|English 3|US|US-English
Table 23-6 — The language list
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
• • • • 247 • • 23 – The language editor Carousel, User Guide
be inserted (since the convention of one line per string cannot on its own support multiline texts).
855 863|No matching valve was found, a balanced port valve or a larger connection may be available instead 862|Strainer 861|WARNING: Due to range limits some input values have changed. Press the forward button again to continue 860|No matching valve was found, a single port valve or a smaller connection may be available instead 859|Application:¦CRLF¦D: Temp. controlled defrost¦CRLF¦F: Fan control¦CRLF¦A: Alarm relay 857|Auto 856|ODF Solder 1/4 in. 855|Flare 6 mm 854|CutRing 6 mm 853|ODF Solder 6mm 852|Flare 1/4 in. 848|A = Alarm¦CRLF¦D = Defrost¦CRLF¦F = Fan 847|PTC 1000 Ohm sensor 846|Pt 1000 Ohm. sensor
Table 23-7 — Sample language file
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 Figure 23-3 — Select languages for export 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.
• • • 248 • • • Carousel, User Guide 23 – The language editor
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 Xoetrope. Please contact [email protected] for more information, the license fee is small.
• • • • 249 • • 23 – The language editor Carousel, User Guide
• • • 250 • • • 24 – Data management Carousel, User Guide
24 Data management
Data is central to most applications and many applications spend much of their code on manipulating data. Carousel 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 incorporated 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 Carousel applications. In earlier chapters (“Data binding” on page 159) 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
Carousel 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. Carousel includes some special features to make working with and integrating database data as simple as possible. Carousel 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” on page 279).
• • 251 • • • • 24 – Data management Carousel, User Guide
Resource access
The Carousel 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 ClassLoaders if so desired or where special circumstances dictate. The project API also provides access to startup parameters. Many of the components within Carousel 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 Carousel. The MVC architecture has already been discussed in connection with data binding (“Data binding” on page 159), so we will not reiterate how it is implemented in Carousel. 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 Carousel is a hierarchical structure. Each node in the model is an instance of the XModel 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 XModel 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 XModel 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 (net.xoetrope.XDataSource) 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 XBaseModel 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.
• • • 252 • • • Carousel, User Guide 24 – Data management
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:
Code Sample 24-1 — A model data configuration file
An example of this static data is:
...
Code Sample 24-2 — A sample dataset
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 Carousel 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.
XBaseModel rootModel = XProjectManager.getModel();
Code Sample 24-3 — Retrieve the root model for the project
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 XPage classes or derivatives the rootModel member variable is predefined for convenience.
XModel model = ( XModel )rootModel.get( “customer/firstname” ); String firstName = ( String )model.get();
Code Sample 24-4 — Retrieving a model node
• • • • 253 • • Carousel, User Guide 24 – Data management
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 XModel or an XBaseModel object to be usable. Once retrieved the value attribute of the model can be retrieved by calling the get method without a parameter.
XModel model = ( XModel )rootModel.get( “customer” ); for ( int i = 0; i < model.getNumChildren(); i++ ) { XModel child = ( XModel )model.get( i ); String name = child.getId(); String value = ( String )child.get(); }
Code Sample 24-5 — Iterating the children of a model
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).
XModel model = ( XModel )rootModel.get( “application” ); int idx = model.getAttribute( “date” ); String appDate = model.getAttribValue( idx );
Code Sample 24-6 — Checking attributes
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.
XModel model = ( XModel )rootModel.get( “customer/firstname” ); model.set( “Joe” );
model = ( XModel )rootModel.get( “customer” ); for ( int i = 0; i < model.getNumChildren(); i++ ) { XModel child = ( XModel )model.get( i ); int idx = child.getAttribute( “somedata“ ); child.setAttribValue( idx, “somevalue” ); }
Code Sample 24-7 — Setting values
Values can be set for the model itself or for any of the attributes. Within the XBaseModel 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 XBaseModel 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:
XModel model = ( XModel )rootModel.get( “customer” ); new XBaseModel( model, “firstname”, “Joe” ); new XBaseModel( model, “surname”, “Bloggs” );
Code Sample 24-8 — Appending one model to another
• • • • 254 • • Carousel, User Guide 24 – Data management
This code shows two new XBaseModel 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:
XModel customerModel = ( XModel )rootModel.get( “customer” ); XModel firstNameMdl = ( XModel )customerModel.get( “firstname” ); model.remove( firstNameMdl );
Code Sample 24-9 — Removing a child model node from its parent
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 XDataSource class.
import net.xoetrope.data.XDataSource; ... public void save() { String contents = getModelText(); saveFile( “C:\XUIapps\test.xml”, contents ); }
private String getModelText() { XBaseModel customerModel = ( XBaseModel )rootMdl.get( "customer" ); StringWriter sw = new StringWriter(); XDataSource.outputModel( sw, customerModel ); return "
public void saveFile( String filename, String contents ) { try { FileOutputStream fos = new FileOutputStream( filename ); OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" ); BufferedWriter bw = new BufferedWriter( osw ); bw.write( contents ); bw.flush(); bw.close(); } catch ( IOException ex ) { ex.printStackTrace(); } }
Code Sample 24-10 — Persisting a model node to file
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
• • • • 255 • • 24 – Data management Carousel, User Guide
saveFile function outputs the text to a FileOutputStream. The saved data file will be saving in the following format.
Code Sample 24-11 — The saved data file
In order to reopen the datafile the XDataSource class can once more be employed.
import net.xoetrope.data.XDataSource; import net.xoetrope.xml.XmlElement; import net.xoetrope.xml.XmlSource;
... public void openFile() { String contents = getFileContents( “c:\XUIapps\test.xml” ); restoreState( contents ); }
private String getFileContents( String filename ) { StringBuffer contents = new StringBuffer(); try { FileInputStream fos = new FileInputStream( filename ); InputStreamReader osw = new InputStreamReader( fos, "UTF8" ); BufferedReader bw = new BufferedReader( osw ); String temp = bw.readLine(); while ( temp != null ) { if ( temp != null ) contents.append( temp ); temp = bw.readLine(); } bw.close(); } catch ( IOException ex ) { ex.printStackTrace(); } return contents.toString(); }
private void restoreState( String contents ) { ( ( XBaseModel )rootModel.get( "xui_state" ) ).clear(); ( ( XBaseModel )rootModel.get( "customer" ) ).clear();
StringReader sr = new StringReader( contents ); XmlElement ele = XmlSource.read( sr );
if ( ele != null ) { XDataSource ds = new XDataSource(); ds.loadTable( ele, rootModel ); }
}
Code Sample 24-12 — Restoring a datasets file programatically
The getFileContents function simply reads the contents of the named file into a StringBuffer and returns its contents. The restoreState function clears the xui_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
• • • 256 • • • Carousel, User Guide 24 – Data management
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. Carousel will wrap new nodes in an instance of XBaseModel 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 XModel 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 Carousel enables similar functionality for rich Java applications. Support for database tables and result sets can be added to XUI through use of the optional database packages (net.xoetrope.optional). These packages allow JDBC result sets to be included in the Carousel data model. The database model nodes can then be bound to UI components in the normal manner. A lightweight pure Java database such as HSQLDB or JavaDB is a good choice for use with a XUI RIA application. These lightweight databases can be used to provide the data an application needs to support off-line or on-the-road usage. Synchronization can be added in some cases or the database could merely be used as a local cache. Having a local database means that the application can access data without having to make a round trip to the server. Databases such as HSQLDB include standalone or in-memory modes that are very fast and allow the database to be bundled as a Jar file. Embedding a database in this way is ideal as it is not necessary to install or configure anything on the client machine. The downside of this is that the database is largely operating in a disconnected mode, without sharing of data between users. Given this limitation you may choose to use the database for long-lived or read-only data while pulling more dynamic data from the Internet on demand. Addings the database DataSource The first task in using database tables in XUI is to add an additional datasource so that your application can locate and load the database data and configuration.
• • • • 257 • • 24 – Data management Carousel, User Guide
The datasource is configured through the startup file with an entry as follows:
XDataSourceClass=net.xoetrope.optional.data.XOptionalDataSource
Code Sample 24-13 — The startup.properties entry for the database datasource
This entry causes the XOptionalDataSource 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:
ModelData=test_datasets.xml
Code Sample 24-14 — The data model configuration file entry for a database datasource
The modeldata file in turn points to a set of data sets:
Code Sample 24-15 — The datasources configuration
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.
Code Sample 24-16 — Configuration of an individual datasource
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. Configuring tables with the NetBeans Within Carousel tables can be added interactively by using the NetBeans Runtime node. Follow the following steps to add a database • Open the project • Open the Visualizer from the Carousel menu • Open the NetBeans Runtime view from the Window menu.
• • • 258 • • • Carousel, User Guide 24 – Data management
• Drill down to the database node • Add an entry for the Database, setting the driver, username, password and other details. See the NetBeans documentation for more details on doing this. • Connect to the database • Select the table of interest, inspecting its data within NetBeans if necessary • Drag the table from the NetBeans Runtime view to the Carousel Visualizer That’s it, the new database table is registered and ready for use in XUI. The new table can be inspected in the visualizer as shown below.
Figure 24-1 — A database table within the Carousel Visualizer
Binding to database tables The simplest way to use tables is to bind UI components such as the Table, Table2 or Combo components (XTable2 is a derivate of the Swing JTable class whereas XTable 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:
Code Sample 24-17 — Binding to a database table
• • • • 259 • • 24 – Data management Carousel, User Guide
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. Drag and Drop data binding Once the visualizer is open and a table or other data source has been registered you can drag and drop it to a component to create a data binding. To create the binding just select the source of the binding within the Visualizer and drag it to the destination component. When the drop occurs a dialog may appear if the binding requires some configuration, an example of this is shown below.
Figure 24-2 — Configuring a binding on drag and drop
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).
public class MyTable extends XPage { XTable myTable;
Code Sample 24-18 — Retrieving a table and updating a UI component
• • • 260 • • • Carousel, User Guide 24 – Data management
public void pageCreated() { myTable = (XTable)findComponent( "myTable" ); myTable.setUpdateModelSelection( true );
myTable2 = (XTable2)findComponent( "yourTable" ); myTable2.setUpdateModelSelection( true );
myCombo = (XComboBox)findComponent( "myList" ); myCombo.doLayout(); }
public void sort() { DatabaseTableModel completeVoltageTable = DatabaseTableModel.getTable( "Voltages"); completeVoltageTable.setDistinct( true ); completeVoltageTable.setOrderField( "ID" ); completeVoltageTable.retrieve();
myTable.setModel( completeVoltageTable ); updateBoundComponentValues(); }
Code Sample 24-18 — Retrieving a table and updating a UI component
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.
public void filter() { DatabaseTableModel voltageTable = new DatabaseTableModel(); // Set the query elements // FROM clause, FIELDS, WHERE clause voltageTable.setupTable( "CS_VOLTAGES", "VOLTAGE_DESCRIPTION, VOLTAGE_MIN, " + "VOLTAGE_MAX", "FREQUENCY=50" ); voltageTable.retrieve(); myTable.setModel( voltageTable ); updateBoundComponentValues(); }
Code Sample 24-19 — Setting the fields to retrieve
Again using this feature we can set individual clauses:
String fromClause = "LEFT_TABLE INNER JOIN RIGHT_TABLE ON LEFT_TABLE.FIELD_A = RIGHT_TABLE.FIELD_1"; String fields = "FIELD_A, FIELD_B, FIELD_2, FIELD_G; String where = "(FIELD_3 LIKE ('"+someValue+"'))"; String order = "FIELD_A";
DatabaseTableModel valveTable = new DatabaseTableModel(); valveTable.setupTable( fromClause, fields, where ); valveTable.setOrderField( order ); valveTable.setDistinct( true ); valveTable.retrieve();
Code Sample 24-20 — Setting individual SQL clauses
• • • • 261 • • 24 – Data management Carousel, User Guide
Finally when this isn’t enough you can set the entire SQL statement using the setSqlStatement:
String sql = "SELECT DISTINCT foo, bar FROM snafu"; String connName = null; // Use the "default" connection
DatabaseTableModel valveTable = new DatabaseTableModel(); valveTable.setSqlStatement( sql, connName, false ); valveTable.retrieve();
Table 24-1 — Set the entire SQL statement
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:
String where = "(A='foo') AND (B='" +someValue+"')";
DatabaseTableModel myTable = new DatabaseTableModel(); myTable.setupTable( "TABLE_A", "A, B, C", null ); myTable.setOrderField( "A, B, C" );
DatabaseTableModel selectedTypes = myTable.findRows( where );
Code Sample 24-21 — Drill down using findRows
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..
Code Sample 24-22 — The database.xml file
• • • 262 • • • Carousel, User Guide 24 – Data management
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 .
DatabaseTableModel dtmRes = null; String params[] = { “testuser”, “22/11/2005”, “80000”, “140000” };
DatabaseTableModel dtm = (DatabaseTableModel)XProjectManager.getModel().get( “InsertApplication” ); dtm.setParams( params );
dtmRes = (DatabaseTableModel)dtm.get(); dtmRes.setDistinct( true ); dtmRes.setDirty( true ); dtmRes.retrieve(); dtmRes.first();
Code Sample 24-23 — Calling a PreparedStatement
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.
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:
Code Sample 24-24 — Referencing multiple databases
• • • • 263 • • 24 – Data management Carousel, User Guide
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. Carousel includes a NamedConnectionManager facility to do this.
NamedConnectionManager connMgr = ( NamedConnectionManager )NamedConnectionManager.getInstance(); connMgr = (NamedConnectionManager)connMgr.reset( connName, dbDriverStr, dbUrlStr, userName, passwordStr );
Code Sample 24-25 — Resetting the database connection
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 XUI 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:
Squirrel SQL hhttp://squirrel-sql.sourceforge.net/
DB Wrench http://www.dbwrench.com/
Aqua Data Studio http://www.aquafold.com/
Table 24-2 — Some free or open source database tools
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 Carousel applications, that is pure Java databases. Carousel 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
• • • 264 • • • Carousel, User Guide 24 – Data management
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 Carousel 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. Carousel includes some features along these lines but this manual does not yet document such features (please contact Xoetrope directly for further information).
Transferring data
Carousel includes a utility to help transfer data between JDBC databases. The utility can be found in the com.xoetrope.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:
Figure 24-3 — The TransferManager requesting a connection string where jdbc:hsqldb:file:/C:/CVS/RS3/resources/rs3db is the name of the HSQLDB instance.
Database synchronization
TODO describe the synchronization options
POJOs
TODO binding to Arrays and other Java data structures 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
• • • • 265 • • 24 – Data management Carousel, User Guide
new class XCollectionTableModel supports the new XTableModel, XRowModel and XFieldModel setup. For example
public class MyTable extends XPage { ...
public MyTable() { String[] names = { "ID", "Title", "Author" };
String[][] rawData = { { "0", "Moby Dick", "Herman Melville" }, { "1", "The Hunchback of Notre Dame", "Victor Hugo" }, { "2", "The Idiot", "Fyodor Dostoevsky" }, { "3", "Ulysses", "James Joyce" }, { "4", "Thus Spake Zarathustra", "Friedrich Nietzsche" }, { "5", "Bleak House", "Charles Dickens" }, { "6", "Mansfield Park", "Jane Austen" }, { "7", "Alice's Adventures in Wonderland", "Lewis Carroll" },
{ "8", "The Republic", "Plato" }, { "9", "Kidnapped", "Robert Louis Stevenson" }, { "10", "On the Duty of Civil Disobedience", "Henry David Thoreau" }, { "11", "The Jungle Book", "Rudyard Kipling" }, { "12", "The Picture of Dorian Gray", "Oscar Wilde" }, { "13", "The Rime of the Ancient Mariner", "Samuel Taylor Coleridge" }, { "14", "Catcher in the Rye", "J. D. Salinger" }, { "15", "The Glass Bead Game", "Herman Hesse" }, };
ArrayList fieldNames = new ArrayList(); for ( String n : names ) fieldNames.add( n );
ArrayList data = new ArrayList(); for ( String[] row : rawData ) { ArrayList rowList = new ArrayList(); for ( String field : row ) { rowList.add( field ); } data.add( rowList ); }
XCollectionTableModel ctm = new XCollectionTableModel( project, "CollectionTable", fieldNames, data ); rootModel.append( ctm ); } ... }
Table 24-3 — Setting up a collection
combines with the page XML below to bind the collection to the 'collectTable' table component.
Table 24-4 — Binding components to a Java collection
• • • 266 • • • Carousel, User Guide 24 – Data management
...
Table 24-4 — Binding components to a Java collection
TODO XPojoModel
Hibernate
TODO using Hibernate
• • • • 267 • • 24 – Data management Carousel, User Guide
• • • 268 • • • 25 – Visualization Carousel, User Guide
25 Visualization
[This is a feature of Carousel] The data model which is built into Carousel 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 Carousel 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 Carousel Editor
Start the Carousel editor in NetBeans and click the Carousel|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 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.
• • 269 • • • • 25 – Visualization Carousel, User Guide
• The toolbar above the attributes table offers a number of extra options for manipulation of the model. Some of these options are also available on the node’s popup context menu.
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.
Table 25-1 — Visualizer tools
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
Code Sample 25-1 — Predifined model
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.
Debugging with the Visualizer
The visualizer makes it easier to debug your Carousel applications as it provides you with an up to date representation of the datamodel at any given time. When you start a debugging
• • • 270 • • • Carousel, User Guide 25 – Visualization
session for a Carousel 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.
• • • • 271 • • Carousel, User Guide 25 – Visualization
• • • • 272 • • 26 – Evaluated attributes and helpers Carousel, User Guide
26 Evaluated attributes and helpers
Evaluated attributes provide a powerful way of extending the scope and range of what Carousel 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” on page 167) 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.
Table 26-1 — Basic attribute evaluation
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:
Code Sample 26-1 — An Evaluated Attribute
• • 273 • • • • 26 – Evaluated attributes and helpers Carousel, User Guide
This example dynamically binds a value to the connSizeSt component. The implementing page should then contain the method
public String getNodeValue( int NodeID ) { ... }
Code Sample 26-2 — A Handler for an Evaluated Attribute
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 XUI 2.0 The attributes can also be defined in classes other than the current page or classes derived from XPage. The syntax for such expressions is as follows:
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
Table 26-2 — Extended attribute declarations
• • • 274 • • • Carousel, User Guide 26 – Evaluated attributes and helpers
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” on page 172. 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 XuiBuilder.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.
• • • • 275 • • 26 – Evaluated attributes and helpers Carousel, User Guide
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:
model.get( "products/software/vendor@name=xoetrope" );
Code Sample 26-3 — A Model Path and Attribute
This searches for a node where the vendor has a name attribute of xoetrope. 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:
model.get( "products/tyres/vendor@name=michelin/size@value=[R14/500]" );
Code Sample 26-4 — A Model Path and a Specified Attribute
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.
• • • 276 • • • Carousel, User Guide 26 – Evaluated attributes and helpers
First of all lets look at the XML:
Table 26-3 — Setting the text via a callback function
Then in the about class the referenced method is implemented:
public class About extends XDialog { public About() { }
/** * Get the text for a popup from an extra file * @param fileName the argument from the XML file */ public String getExtraText( String fileName ) { // Get the default language from the preferences Preferences rsPrefs = Preferences.userNodeForPackage( Welcome.class ); String defLangCode = rsPrefs.get( "defaultLanguage", "en" );
// Build the path to the file - use a separate sub folder for each language String filePath = defLangCode + File.separator + fileName + ".html"; try { // Open and read the file Reader r = project.getBufferedReader( filePath );
StringBuffer text = new StringBuffer(); int BUF_LEN = 1024; int len = -1; char buffer[] = new char[ BUF_LEN ]; while (( len = r.read( buffer, 0, BUF_LEN )) >= 0 ) text.append( buffer, 0, BUF_LEN );
// Now return the text return text.toString(); } catch ( Exception e ) { e.printStackTrace(); }
// The resource was not loaded for some reason return translate( "Missing resource" ); } }
Table 26-4 — Implementingthe evaluated method
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
• • • • 277 • • 26 – Evaluated attributes and helpers Carousel, User Guide
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:
Figure 26-1 — Text loaded via an evaluated attribute from an external file
• • • 278 • • • 27 – Introduction to routes and services Carousel, User Guide
27 Introduction to routes and services
Routes and services within Carousel offer an easy to use way of integrating enterprise level functionality into an applications. Typically routes and services are used to add client-server communications and backend integration to an application. The concept of a service within Carousel is designed to make it easy to embed a piece of functionality into the data model. A function can be evaluated and the data it returns can be considered to be part of the model just as though it were any other data node. In this way the location of the function and the details of the invocation can be hidden from the programmer. The service concept is sufficiently flexible to allow a wide variety of functional components to be embedded in the model.
Figure 27-1 — A service node embedded in the data model.
Taking this abstraction a little futher we can chain such services together to build more complex functionality. Each service node within the hierarchy can do one specific task and
• • 279 • • • • 27 – Introduction to routes and services Carousel, User Guide
delegate to the next service node for additional features. Because the role of each service node is clear cut it helps simplify implementation of that service.
Figure 27-2 — Chaining service nodes to form a route to a service.
Frequently services are used to transform, encode or somehow process data. The location of the original data can be hidden within a service and an individual service can be replaced so that high level logic, such as your business logic is freed from low level details that might otherwise give unwanted bindings or even hard coding of platform specific details. In an enterprise level application these services play a key role, allowing client-server communications to take place. For example, a commonly used service is the HttpRequestService, this service allows that application to make a request to a webserver. The packaged services in Carousel thus provides much of the infrastructure needed to build powerful applications without placing lots of demands on the application programmer.
Figure 27-3 — Client and server routes encode and decode data for transmission over the net.
• • • 280 • • • Carousel, User Guide 27 – Introduction to routes and services
Routes then, are simple a layering together of a number of services so as to enable communications and data retrieval. A route might well consist of services for communications, security, authentication and so on. The difference from a pure collection of services is that a route is intended to be connected to end-points for implementation and consumption of the service. The route provides the common functionality needed to connect the end-points. Thus one route might well be used for a number of different services. Routes in Carousel are used to control the flow, manipulation and transformation of data. A route is made up of one or several serives nodes or ServiceProxy objects which can end up at a web server on a filesystem or wherever the developer sees fit.
Setting up a routing file
In Carousel, the routes are specified in a routing file which is referenced from the datasets.xml file.
Code Sample 27-1 — Datasets.xml file referencing a routing file
The reference to the routing file is the last entry in the datasets.xml file. In order for Carousel to process this file as a routing file the type attribute needs set to routing. The startup property XDataSourceClass needs to be set to net.xoetrope.optional.data.XOptionalDataSource in the startup properties file.
XDataSourceClass=net.xoetrope.optional.data.XOptionalDataSource
Code Sample 27-2 — The required startup property
Next, the routing file needs to be created. In this example, the route will pass information to a file on the client machine. There is a single ServiceProxy layer within the route which will take care of opening and saving the information.
Code Sample 27-3 — The new routes.xml file.
This file specifies a single route with the name FileSave which will be used when saving information to a file.
• • • • 281 • • Carousel, User Guide 27 – Introduction to routes and services
Setting up the Services file
The services.xml file needs to define services which will refer to the routes defined in the routes.xml file.
Code Sample 27-4 — The services.xml file
The services.xml file needs to be referred to from the datasets.xml file as follows...
Code Sample 27-5 — The services.xml file
The type attribute needs to be defined as service in order for Carousel to process it as such
Saving a file using the FileSave route
In this example the FileSave route will be used to save the state of the xui_state/ mortapp node in the model. The following is a page which is updating some personal information about a mortgage applicant with component bindings..
Code Sample 27-6 — A page with some bindings
• • • • 282 • • Carousel, User Guide 27 – Introduction to routes and services
package net.xoetrope.mortgage;
import java.io.StringWriter; import javax.swing.JFileChooser; import net.xoetrope.data.XDataSource; import net.xoetrope.optional.service.ServiceContext; import net.xoetrope.optional.service.ServiceProxyArgs; import net.xoetrope.optional.service.XServiceModelNode; import net.xoetrope.xui.XProjectManager; import net.xoetrope.xui.data.*; import com.xoetrope.service.file.FileSave;
public class Finish extends XPage {
public void save() { String filename = getFileName(); if ( filename != null ) { XBaseModel model = ( XBaseModel ) rootModel.get( "FileSaveService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_SAVE ); args.setPassParam( FileSave.ARG_NAME_CONTENTS, getAppContent() ); args.setPassParam( FileSave.ARG_NAME_FILENAME, filename ); Object result = node.get( context ); } }
private String getAppContent() { XBaseModel mdl = ( XBaseModel )rootModel.get( "xui_state/mortapp" ); StringWriter sw = new StringWriter(); XDataSource.outputModel( sw, mdl ); return "
private String getFileName() { JFileChooser chooser = new JFileChooser(); if ( chooser.showSaveDialog( this ) == JFileChooser.APPROVE_OPTION ) { return chooser.getSelectedFile().getAbsolutePath(); } return null; } }
Code Sample 27-7 — Saving via the FileSave route
The save function retrieves the location and name of the file which is to be saved by calling the getFileName() function. Next it retrieves the FileSaveService model node from the model after which it casts its value to a XServiceModelNode object. Next a ServiceContext object is created. This object is used to pass and return parameters through the route. In this case the FileSave arguments need to be set. These are set by using the public static String values of the FileSave class. The contents of the file are specified by making a call to the getAppContent() function. The getAppContent() function retrieves the xui_state/mortapp node from the model and outputs it to a StringWriter using the XDataSource.outputModel function. It then wraps the content in a Datasets node so that it can be reopened.
• • • • 283 • • 27 – Introduction to routes and services Carousel, User Guide
When this code is invoked the resulting file will look something like the following...
Code Sample 27-8 — The saved content
This file contains information from bindings on other pages, but it’s possible to see the paths which were specified in the page declaration earlier.
• • • 284 • • • Carousel, User Guide 27 – Introduction to routes and services
Opening a file using the FileSave route
Now that the file is saved, the developer may wish to allow it to be opened again. The same route will be used to carry out this functionality but with some different parameters..
public void open() { String filename = getFileName(); if ( filename != null ) { ( ( XBaseModel )rootMdl.get( "xui_state/mortapp" ) ).clear(); ( ( XBaseModel )rootMdl.get( "mortapp" ) ).clear();
XOptionalDataSource ds = new XOptionalDataSource();
XBaseModel model = ( XBaseModel ) rootModel.get( "FileSaveService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_OPEN ); args.setPassParam( FileSave.ARG_NAME_FILENAME, filename ); Object result = node.get( context ); StringReader sr = new StringReader( ( String ) args.getReturnParam( FileSave.ARG_NAME_CONTENTS ) ); XmlElement ele = XmlSource.read( sr );
if ( ele != null ) ds.loadTable( ele, rootMdl ); ds.loadTable( ele, ( XModel )rootMdl.get( “xui_state”) );
updateBindings(); } }
private String getFileName() { JFileChooser chooser = new JFileChooser(); if ( chooser.showOpenDialog( this ) == JFileChooser.APPROVE_OPTION ) { return chooser.getSelectedFile().getAbsolutePath(); } return null; }
Code Sample 27-9 — Opening a flle via the FileSave route
Again, the name of the file to be opened is specified by using the file chooser in the getFileName() function. The mortapp node will be populated so it is a good idea to first clear it using the clear() function. The same should be done with the xui_state/ mortapp node. The ServiceContext is constructed again except this time the contents parameter is not set and the mode is open instead of save. When the call returns it is now possible to get the contents of the file from the return parameters of the ServiceContext object. Once retrieved, the XML is used to populate the model. The application will now bind once more to the data in the file.
Creating a custom ServiceProxy class
The FileSave ServiceProxy is a fairly useful way of saving to a file in a consistent way but does not provide the developer with anything that they could not have done quite easily themselves. In order to gain some real benefit from routing several ServiceProxy layers are usually employed to manipulate or transform the passed data. In the following example
• • • • 285 • • 27 – Introduction to routes and services Carousel, User Guide
the work which was done in moving the data in and out of the model will be taken care of by a custom ServiceProxy class.
package net.xoetrope.mortgage.service;
import net.xoetrope.optional.service.ServiceContext; import net.xoetrope.optional.service.ServiceProxy; import net.xoetrope.optional.service.ServiceProxyException;
public class StateManager extends ServiceProxy {
/** * The overloaded call for the ServiceProxy * @param method the name of the service * @param args the service arguments */ public Object call( String method, ServiceContext context ) { try { return callNextProxy( method, context, null ); } catch ( ServiceProxyException e ){ return null; } }
}
Code Sample 27-10 — The custom StateManager class
This class extends the ServiceProxy class and by doing so also needs to overload the abstract call method. This call receives the name of the service as its first parameter and the ServiceContext which was constructed from the calling code as its second parameter. The code above does no more than pass the call on to the next ServiceProxy in the route and return its outcome. In order for this class to be invoked when the FileSaveService is called the routes.xml file needs to be amended.
Code Sample 27-11 — The StateManager ServiceProxy as part of the route
• • • 286 • • • Carousel, User Guide 27 – Introduction to routes and services
The StateManager class is called before the FileSave service proxy whenever the FileSaveService is called. Now the model manipulation code can be moved out of the calling classes and into this service proxy layer.
public class StateManager extends ServiceProxy {
public static final String ARG_NAME_MODELPATH = "modelpath"; private XModel rootMdl = XProjectManager.getModel(); /** * The overloaded call for the ServiceProxy * @param method the name of the service * @param args the service arguments */ public Object call( String method, ServiceContext context ) { ServiceProxyArgs args = context.getArgs(); String mode = ( String )args.getPassParam( FileSave.ARG_NAME_MODE ); String modelPath = ( String )args.getPassParam( ARG_NAME_MODELPATH ); try { Object ret = null; if ( mode.compareTo( FileSave.ARG_VALUE_OPEN ) == 0 ) { // The file is being opened so the returned content needs to populate the model ( ( XBaseModel )rootMdl.get( "xui_state/" + modelPath ) ).clear(); ( ( XBaseModel )rootMdl.get( modelPath ) ).clear();
ret = callNextProxy( method, context, null );
StringReader sr = new StringReader( ( String ) args.getReturnParam( FileSave.ARG_NAME_CONTENTS ) ); XmlElement ele = XmlSource.read( sr );
if ( ele != null ) { XOptionalDataSource ds = new XOptionalDataSource(); ds.loadTable( ele, rootMdl ); } } else if ( mode.compareTo( FileSave.ARG_VALUE_SAVE ) == 0 ) { // The file is being saved and the contents of the target model need to be output XBaseModel mdl = ( XBaseModel )rootModel.get( “xui_state/” + modelPath ); StringWriter sw = new StringWriter(); XDataSource.outputModel( sw, mdl ); args.setPassParam( FileSave.ARG_NAME_CONTENTS, "
}
Code Sample 27-12 — The fully functional StateManager class
The StateManager class now takes care of retrieving and setting the model data. The ARG_NAME_MODELPATH member variable is defined for convenience when setting the appropriate argument in the calling code. The first thing that is done in the call method is that the context is checked for the mode of the call. The FileSave variable is used for this so as to ensure consistency. If the mode is to open the file the target model node is cleared as well as the xui_state model node. The call is then passed over to the next proxy which is the FileSave service proxy in this case as defined in the routes.xml file. Once returned the contents return parameter is used to populate the model. If the mode is to save the file target model is output to a StringWriter which is used to set the ARG_NAME_CONTENTS pass parameter. The next proxy is called and control returns to the calling code.
• • • • 287 • • 27 – Introduction to routes and services Carousel, User Guide
The open and save code can now be cleaned up by removing the model manipulation code
public void save() { String filename = getFileName(); if ( filename != null ) { XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "FileSaveService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_SAVE ); args.setPassParam( FileSave.ARG_NAME_FILENAME, filename ); args.setPassParam( StateManager.ARG_NAME_MODELPATH, "mortapp" ); node.get( context ); } }
Code Sample 27-13 — The modified save code
All that is left in this code is the setting up of the XServiceProxyNode, the service context and its parameters. An extra line has been added which sets the ARG_NAME_MODELPATH parameter which is used to address the correct part of the model.
public void open() { String filename = getFileName(); if ( filename != null ) { XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "FileSaveService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
args.setPassParam( FileSave.ARG_NAME_MODE, FileSave.ARG_VALUE_OPEN ); args.setPassParam( FileSave.ARG_NAME_FILENAME, filename ); args.setPassParam( StateManager.ARG_NAME_MODELPATH, "mortapp" ); node.get( context );
updateBindings(); } }
Code Sample 27-14 — The modified open code
Again, the code is much simplified. The code sets up the XServiceModelNode, creates the service context and its parameters. The updateBindings method of the XPage is called in case any components on the page are bound to the affected part of the model.
Building routes visually with Carousel
The Carousel plugin for KalIDEoscope allows the developer to create the routing and services files automatically as well as providing the ability to build up the routing layers visually.
• • • 288 • • • Carousel, User Guide 27 – Introduction to routes and services
When the Carousel plugin has been loaded the files tab on the project editor will show the new settings as shown below.
Figure 27-4 — The Carousel properties
These are the files for the services and routes as referred to earlier. There are other files referenced here which could do with some explanation.
Routes This is the routing file which will be used by the client application. It will be created automatically by Carousel in the resources directory.
Services This is the services file which will be used by the client application. It will be created automatically by Carousel in the resources directory.
ServerRoutes This is the routing file which will be used by Carousel on the server side. It will be created automatically by Carousel in the resources/server directory.
ServerServices This is the services file which will be used by Carousel on the server side. It will be created automatically by Carousel in the resources/server directory.
ServerDatasets This is similar to the client side datasets file. It will be used on the server by Carousel to reference the routing and services files. This file will be created in the resources/server directory.
ServerStartup Just like the client, the server-side Carousel component requires this startup file in order to identify the ModelData property. This file will be created in the resources/server directory.
CustomRouteDefs As custom ServiceProxy classes are added to a project they can be referenced in this configuration file so that they can be added to routes via the routes menu. This file can also be moved between projects to build up a library of custom ServiceProxy classes. This file will be created in the project root directory.
Table 27-1 — Carousel project property files
• • • • 289 • • 27 – Introduction to routes and services Carousel, User Guide
In order to access the route editor within KalIDEoscope, click the Carousel | Services | Route Manager menu as shown below.
Figure 27-5 — The Services menu
The routes editor will appear as in the screenshot below.
Figure 27-6 — The Route Manager
To create a new route on the client side, right click the Client node in the Routes tree and click New Route from the popup menu. A route called New Route will appear under the Client node. The new route can be renamed by selecting it and pressing F2 or by clicking the Rename Route icon in the Route Manager toolbar. To add say, the FileSave ServiceProxy to the new route right-click the route in the Routes tree and from the popup menu select New ServiceProxy Layer. This menu will provide a list of registered ServiceProxy objects. Select com.xoetrope.service.file.FileSave from the submenu and it will appear under the route node.
Figure 27-7 — ServiceProxy classes
• • • 290 • • • Carousel, User Guide 27 – Introduction to routes and services
Now the Route Manager can be used to create the custom StateManager ServiceProxy class as described earlier in this chapter. Click the Create a custom ServiceProxy layer button on the Route Manager toobar and the new custom ServiceProxy dialog will appear as below.
Figure 27-8 — Custom ServiceProxy dialog The default package for the project’s XPage classes will have the services package appended to it and will appear as the class name so the StateManager class name needs to be added to it. The new ServiceProxy class can extend on of the existing ServiceProxy classes which are listed in the Base Class dropdown. Select the Create the class checkbox and click OK and the new class will be created automatically and opened for editing in the netBeans editor. Now the new StateManager route can be added to the route by right clicking the route and clicking New ServiceProxy Layer in the popup menu as shown below.
Figure 27-9 — The new StateManager class in the ServiceProxy menu
• • • • 291 • • 27 – Introduction to routes and services Carousel, User Guide
The service for this route now needs to be defined and this is done through the Service Manager which can be accessed by clicking the Carousel | Services | Service Manager menu. The Service Manager will appear as below.
Figure 27-10 — The Service Manager window
Right-click on the Client node in the Services tree and click New service on the popup menu. A new node with the text new service will appear below the Client node which can be renamed by pressing F2 or by clicking the Rename the selected service button on the toolbar.
Figure 27-11 — Creating a service with the Service Manager
Save the project and the routes and services files will be saved as previously without the need to hand-code them. It is worth looking at the newly created customroutedefs.xml file in the project root folder as mentioned previously.
Code Sample 27-15 — The customroutedefs.xml file
• • • 292 • • • Carousel, User Guide 27 – Introduction to routes and services
This file is used by Carousel to identify custom ServiceProxy classes which are registered for the project so that they can appear in the ServiceProxy list in the popup menu. This file can easily be transferred between projects along with the custom ServiceProxy classes.
Summary
Moving data manipulation code out of project code and into service proxy layers cleans up the project code and creates a consistent way of persisting and retrieving data. These layers can be built into a set of libraries depending on the requirements of the projects being developed. It is then much easier to replace functionality and to add other layers. A simple encryption layer could, for example, be added between the StateManager and FileSave service proxy classes to encrypt data as it is passed back and forth. By using the editor to build up routes and services and to create custom ServiceProxy classes much of the manual work of setting up the route definitions is automated for you.
• • • • 293 • • 27 – Introduction to routes and services Carousel, User Guide
• • • 294 • • • 28 – Advanced routes and services Carousel, User Guide
28 Advanced routes and services
[This is a feature of Carousel] This chapter will look at how routes and services can be used to hook the XUI client application up to webservers. The first piece of functionality to be covered is handling session management and user authentication. In order to do this the built-in servlet session and authentication service proxy classes are used to make up a route.
Session Management and User Authentication
Code Sample 28-1 — The client side routes.xml file
This route is made up of two service proxy layers. The first will take care of handling session management with a servlet engine. The second is a service proxy which passes the routed data to a servlet. The service proxy attributes should now be pointed out. The selfmanaged attribute of the SessionManager lets the class know that it should take care of storing and forwarding the session key. The modelid attribute informs the SessionManager of the path within the datamodel where the session id is to be stored once retrieved. The URL attribute of the XHttpClientServiceProxy class informs it of the URL which will receive the routed data. In order for the servlet engine to receive the requests, the datasets, routes, services and starup file need to be packaged and deployed to the servlet engine along with the Carousel.jar library. The server needs its own startup.properties file and its own routes file. The startup file which the XServiceServlet class uses by default is carouselserver.properties. If this needs to be different the XServiceServlet will need to be subclassed and its protected startupFile variable will need to be set.
ModelData=serverdatasets.xml
Code Sample 28-2 — The carouselserver.properties file
• • 295 • • • • 28 – Advanced routes and services Carousel, User Guide
The startup file thus points to another file detailing the data sources: .
Code Sample 28-3 — The serverdatasets.xml file
If the KalIDEoscope editor is being used to setup the routes and services these files will be created in the resources/server directory.
Code Sample 28-4 — The serverroutes.xml file
The server version of the file has a single service proxy layer as compared to its client counterpart which has two. The code to setup and call the service now needs to be written.
XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletService" ); XServiceModelNode node = ( XServiceModelNode ) model.get();
ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
Object result = node.get( context ); System.out.println( args.getReturnParam( SessionManager.ARG_NAME_RETURN_SESSION ) );
Code Sample 28-5 — Code to call the ServletRoute service
There are no parameters required for the call and the established session key can be output using the return parameters. If this route is used again it will take care of synchronising the client with the already established server session. If the developer wishes to take over control of handling the session key the selfmanaged attribute should be set to false on the client routes file. The call now needs to be changed to pass the session key and to store it when it returns. It is important to note that the SessionManager will work with the selfmanaged attribute set to true only if the same route is used every time for contacting the webserver. If the SessionManager is going to be used in several routes then the selfmanaged attribute should be set to false and the developer should taked care of storing and passing the sessionkey for each call. Alternatively the modelid attribute can be set which informs the
• • • 296 • • • Carousel, User Guide 28 – Advanced routes and services
SessionManager of the path into the data model where the session key is stored between calls.
private String sessionKey; private void call() { XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletService" ); XServiceModelNode node = ( XServiceModelNode ) model.get();
ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs(); args.setPassParam( SessionManager.ARG_NAME_PASS_SESSION, sessionKey );
Object result = node.get( context ); sessionKey = ( String ) args.getReturnParam( SessionManager.ARG_NAME_RETURN_SESSION ); }
Code Sample 28-6 — Modified code to pass and store the sessionkey
Now that the session is being synchronised between the client and server, user authentication can be built into the route. There is a user authentication service proxy built into Carousel which needs to be subclassed in order to be used on the server side. The default class can be used on the client. This subclass is defined below.
package net.xoetrope.mortgage.services;
import com.xoetrope.service.AuthenticationService;
public class LogonService extends AuthenticationService { protected boolean checkCredentials( String userID, String password ) { if ( userID.compareTo( "joe" ) == 0 ) return true; else return false; } }
Code Sample 28-7 — The LogonService service proxy
checkCredentials() is the overloaded function which is used on the server to check the logon credentials of the passed userid and password. Of course, a little more work needs to be done in order to carry out a meaningful authentication. The modified client and server route files are as follows:
Code Sample 28-8 — The client side routes.xml file
• • • • 297 • • Carousel, User Guide 28 – Advanced routes and services
and on the server side the equivalent file is:
Code Sample 28-9 — The server side serverroutes.xml file
The client side route uses the basic authentication service proxy whereas its counterpart on the server uses the LogonService subclass. The LogonService could just as easily be used on the client but the developer may not want to make their server logic available for decompilation.
Creating a custom client-server service proxy
This next example will take the FileSave and StateManager service proxy classes discussed in the previous chapter and merge them in a route with the SessionManager and LogonService service proxy classes in order to save the application data on a webserver. First modify the two route files to declare the new route which is to be utilised.
Code Sample 28-10 — The modified client-side routes.xml file
again, the server-side equivalent is:
Code Sample 28-11 — The modified server-side serverroutes.xml file
The StateManager layer is omitted from the server side route because it only manipulates data on the client. So now it is possible, using the same service proxy classes that were used to save data on the client machine, to persist that same data to the server in a seamless way. There is a new service proxy class, ServletFileSave, defined which takes care of the server side saving of the data. There is another difference between the client and server route
• • • • 298 • • Carousel, User Guide 28 – Advanced routes and services
definitions and that is the savepath attribute which is set for the ServletFileSave service proxy on the server. This is the location where files will be stored on the server.
package net.xoetrope.mortgage.service;
import com.xoetrope.service.file.FileSave; import java.io.File; import java.util.Hashtable; import net.xoetrope.optional.service.ServiceContext; import net.xoetrope.optional.service.ServiceProxyArgs; import net.xoetrope.optional.service.ServiceProxyException; import net.xoetrope.optional.service.XRouteManager;
public class ServletFileSave extends FileSave {
private String savePath = null;
public Object call( String method, ServiceContext context ) { Object ret = null; ServiceProxyArgs args = context.getArgs(); try { String mode = ( String )args.getPassParam( ARG_NAME_MODE ); if ( side == XRouteManager.CLIENT_SIDE ) { ret = callNextProxy( method, context, null ); } else { if ( mode.compareTo( ARG_VALUE_SAVE ) == 0 ) saveFile( savePath + File.separatorChar + args.getPassParam( ARG_NAME_FILENAME ).toString(), args.getPassParam( ARG_NAME_CONTENTS ).toString() ); else if ( mode.compareTo( ARG_VALUE_OPEN ) == 0 ) args.setReturnParam( ARG_NAME_CONTENTS, openFile( savePath + File.separatorChar + args.getPassParam( ARG_NAME_FILENAME ).toString() ) ); ret = callNextProxy( method, context, null ); } } catch ( ServiceProxyException e ) { e.printStackTrace(); } return ret; }
public void setAttributes( Hashtable attribs ) { if ( side == XRouteManager.SERVER_SIDE ) savePath = ( String )attribs.get( "savepath" ); } }
Code Sample 28-12 — The ServletFileSave class
The setAttributes function overloads the same function in the service proxy super class and is called when the route is first created. In this example, the savePath variable will only be set when the route is on the server side. In the call function a similar check is made to see which side the service proxy is operating on. On the client side the call is merely proxied to the next service proxy and on the server side the saveFile and openFile methods of the super class are used to save and open the files. The only change in the calling code is made to the name of the service which is called.
XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletService" );
Code Sample 28-13 — The change to the code setting up the service.
• • • • 299 • • 28 – Advanced routes and services Carousel, User Guide
Using datasets to attach to a database
This section will take a look at how to use a datasets file to define a database connection and how to use it to retrieve data from the database. The first thing to be done is to create the database.xml file.
Code Sample 28-14 — The database.xml file
The datasets file needs to contain at least one Connection entry which is used to establish the database connection. In this example the database being used is a MySQL database on the localhost machine. It will be located on the webserver and the database.xml file needs only to be deployed on the server. The Connection entry has several attributes which are explained as follows:
Attribute Description
id The name of the ‘Connection’. This is used by the ‘ResultSet’ elements so that the correct connection is used in retrieving data.
driver The jdbc driver class which is used to establish connections
url The database connection URL
user The user which is to be used to connect to the database
pwd The password of the user
Table 28-1 —
There are two ResultSet declarations defined both of which are given an id. The conn attribute defines the Connection against which the query is to be run. In the two ResultSet elements defined, PreparedStatements are being used. Now that the database.xml file has been defined, it needs to be referenced by the datasets.xml file.
Code Sample 28-15 — The amended datasets.xml file
The new entry is defined as type database and uses the filename attribute to reference the new file. This example will look at how the mortgage application can used in conjunction with the database to send and retrieve data. The first query to be implemented is SearchMortgages which will search the existing mortgage applicants and return the
• • • 300 • • • Carousel, User Guide 28 – Advanced routes and services
results to the client application. Both of the route files need to be amended to use Carousel’s QueryService which will take care of running the SQL queries.
Code Sample 28-16 — The clientside routes.xml file
Code Sample 28-17 — The serverside serverroutes.xml file
Now the code which calls the new route can be written.
XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs(); args.setPassParam( QueryService.ARG_NAME_QUERYID, "SearchMortgages" ); args.setPassParam( QueryService.ARG_NAME_NUMPARAMS, 1 ); args.setPassParam( QueryService.ARG_NAME_PARAMNAME + "0", "searchtext" ); args.setPassParam( QueryService.ARG_NAME_PARAMVALUE + "0", "%" + searchEdit.getText() + "%" );
Object result = node.get( context ); System.out.println( args.getReturnParam( QueryService.ARG_NAME_RESULTSET ) );
Code Sample 28-18 — Code to call the SearchMortgages Query
The XServiceModelNode and ServiceContext objects are accessed in the usual way. In this example the AuthenticationService and the SessionManager service proxy classes both have their ‘selfmanaged’ attributes set to true so there is no need to pass the userid and sessionkey parameters. All of the parameters referenced in the code are for the QueryService service proxy. These parameters meaning is defined as follows:
Parameter name Description
ARG_NAME_QUERYID The name of the Query as defined in the database.xml file which is to be executed.
ARG_NAME_NUMPARAMS The number of parameters required to populate the PreparedStatement
ARG_NAME_PARAMVALUE This is the name which is used for each of the PrepartedStatement parameters. It must be concatenated with the zero based index of the parameter being set
ARG_NAME_PARAMVALUE The value of the PreparedStatement parameter. It must be concatenated with the zero based index of the parameter being set
Table 28-2 — The QueryService parameters
• • • • 301 • • 28 – Advanced routes and services Carousel, User Guide
Parameter name Description
ARG_NAME_RESULTSET The returned data in XML format
Table 28-2 — The QueryService parameters
When the new serverroutes.xml is deployed to the server the client application can be run and the ARG_NAME_RESULTSET parameter will contain data in the following format:
Code Sample 28-19 — The data return in the ARG_NAME_RESULTSET parameter
This data can be used by the developer in whatever way they see fit. It can be loaded into an XML document or loaded into the model using the XOptionalDataSource.loadTable call but in order to expand this example the data will now be used to populate a swing table. In order to make this easier a new service proxy, TransformService, will be introduced. The two route files need to be amended to make use of this service proxy layer.
Code Sample 28-20 — The clientside routes.xml file
Code Sample 28-21 — The serverside serverroutes.xml file
The TransformService will carry out a transformation on the XML data returned in the ARG_NAME_RESULTSET parameter. The transformation will be carried out by a file with the
• • • 302 • • • Carousel, User Guide 28 – Advanced routes and services
same name as the ARG_NAME_QUERYID. So in this case the transformation will be carried out by a file called SearchMortgages.xsl as shown below:
Code Sample 28-22 — The SearchMortgages.xsl file
The effect that this transformation will have on the XML is as follows:
Code Sample 28-23 — The data transformed through the SearchMortgages.xsl file
• • • • 303 • • 28 – Advanced routes and services Carousel, User Guide
This is the format which is used by the XTable2 class in the XUI optional package. In order to carry out this binding, the calling code needs to be changed once more.
public void doSearch() { XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletService" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); XBaseModel searchMdl = ( XBaseModel )XProjectManager.getModel().get( "searchresults/applicants" ); searchMdl.clear(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs(); args.setPassParam( QueryService.ARG_NAME_QUERYID, "SearchMortgages" ); args.setPassParam( QueryService.ARG_NAME_NUMPARAMS, 1 ); args.setPassParam( QueryService.ARG_NAME_PARAMNAME + "0", "searchtext" ); args.setPassParam( QueryService.ARG_NAME_PARAMVALUE + "0", "%" + searchEdit.getText() + "%" );
node.get( context ); System.out.println( args.getReturnParam( QueryService.ARG_NAME_RESULTSET ) );
addResultsTable( searchMdl ); repaint(); doLayout(); }
private void addResultsTable( XBaseModel mdl ) { if ( tbl != null ) remove( tbl.getParent().getParent() ); tbl = new XBaseTable( this, 200, 100, 400, 200 ); XTableModelAdapter tma = new XTableModelAdapter( mdl ); tbl.setTableModelAdapter( tma, null ); tbl.setHeaderStyle( "tableheader" ); repaint(); }
Code Sample 28-24 — Use the transformed XML to populate the XTable2 table
The addResultsTable takes the ARG_NAME_RESULTSET xml and uses it to construct an XTableModelAdapter. A style is applied to the table and the page is repainted and the doLayout function is called.
Binding a ResultSet to screen components
The user will now want to select an applicant from the table and open up the individual record for editing. The second ResultSet is used to retrieve the data and its data is
• • • 304 • • • Carousel, User Guide 28 – Advanced routes and services
transformed once more using the TransformService. The code to call the service is as follows:
public void openApplication() { if ( wasMouseClicked() ) { int row = tbl.getSelectedRow(); XBaseModel mdl = tbl.getRow( row ); String appID = ( String )( ( XBaseModel ) mdl.get( "application_id" ) ).get(); XModelHelper.setTempVar( "selectedMortgage", appID ); XBaseModel model = ( XBaseModel ) XProjectManager.getModel().get( "ServletQuery" ); XServiceModelNode node = ( XServiceModelNode ) model.get(); XBaseModel mortMdl = ( XBaseModel )XProjectManager.getModel().get( "MortApp" ); mortMdl.clear(); ServiceContext context = new ServiceContext(); ServiceProxyArgs args = context.getArgs();
args.setPassParam( "Mode", "Query" ); args.setPassParam( "QueryType", "RetrieveMortgage" ); args.setPassParam( "ModelPath", "currentISO" ); args.setPassParam( "numParams", 1 ); args.setPassParam( "paramname0", "appID" ); args.setPassParam( "paramvalue0", appID ); Object result = node.get( context ); XModelHelper.setTempVar( "currentID", mortMdl.get( 0 ).getId() ); pageMgr.showPage( "Personal" ); } }
Code Sample 28-25 — Code to retrieve an applicants details
A new XSL file needs to be defined for the RetrieveMortgage query which will transform the result set data into a format which can be used by the front end.
Code Sample 28-26 — XSL file to transform the RetriveMortgage query
• • • • 305 • • 28 – Advanced routes and services Carousel, User Guide
When the data is returned the screens will populate with the data which has been returned by the query.
Using KalIDEoscope to edit the routes
As seen in the previous chapter, the KalIDEoscope editor can be used to build up routing information for a project. It is now worth looking at the steps involved in setting up the routes at the beginning of this chapter using the editor. Open the route manager and set up the client side route as shown below.
Figure 28-1 — The client side ServletRoute route definition
Now the corresponding server side route can be set as shown below.
Figure 28-2 — The server side ServletRoute route definition
IF the project is saved now the client route file resources/routes.xml will contain the ServletRoute with layers for SessionManager and XHttpClientServiceProxy and the server routes file resources/server/serverroutes.xml will contain the ServletRoute with a single layer for the ServerSessionManager.
• • • 306 • • • Carousel, User Guide 28 – Advanced routes and services
In order to create the custom Authentication service, right click on the server side ServletRoute, click New ServiceProxy Layer and select net.xoetrope.service.AuthenticationService from the list of ServiceProxy classes. When the AuthenticationService is selected from the list of ServiceProxy classes the New ServiceProxy dialog is shown as below.
Figure 28-3 — Creating the custom LogonService ServiceProxy
The reason for this is that the AuthenticationService class is defined as an abstract class and as such needs to be subclassed in order to be used. The reason for this is that the project specific authentication code needs to be written for the checkCredentials function. The default classes package has the services package appended to it and is shown in the class name text field. The new class name should be appended to the package name. The Base Class dropdown is automatically set to com.xoetrope.service.AuthenticationService. The attributes which have been inherited from the base class are listed in the attributes section. This is so that these attributes can also be set for the new class. It is possible to add custom attributes if necessary. Select the Create the class checkbox and click OK and the new class will be created and opened for you in the netBeans editor. If you check the ServerRoute you will see that the new LogonService ServiceProxy has been added as a layer in the route. The checkCredentials function now needs to be overloaded in order to take care of the server side authentication.
• • • • 307 • • 28 – Advanced routes and services Carousel, User Guide
Now it’s just a simple matter of adding the new LogonService as a layer into the client ServerRoute. Save the project and open the customroutedefs.xml file and it should appear something like follows.
Code Sample 28-27 — The customroutedefs.xml file
• • • 308 • • • 29 – Working with components Carousel, User Guide
29 Working with components
At the heart of XUI and Carousel is the component factory. The built-in component factory constructs the XUI 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. Carousel allows component factories to be added and hence it can accommodate a wide range of components apart from the built-in set. Carousel 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 Carousel’s repeat element is implement via a special component factory.
Using the registry editor
The Integrated Component Registry Editor is a new feature in XUI 2.0 The component registry editor can be accessed by clicking the ‘*’ button in the component palette toolbar. Almost all of Carousel’s non-core components are added via this registry so you can see how the properties of the indvidual components are configured.
• • 309 • • • • 29 – Working with components Carousel, User Guide
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.
Figure 29-1 — Choose a Jar file 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:
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.
Code Sample 29-1 — Component configuration modes
• • • 310 • • • Carousel, User Guide 29 – Working with components
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.
Figure 29-2 — Select the component properties 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 KALIDEOSCOPE and Carousel 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. Carousel itself uses the registry for loading component Jar files and therefore an individual application may use components registered via several configuration files. Carousel and KALIDEOSCOPE 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 Carousel and XUI 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.
• • • • 311 • • Carousel, User Guide 29 – Working with components
Installing a component factory
The factory needs to be registered with the main class, this is done through the startup.properties file.
NumComponentFactories=1 ComponentFactory0=net.xoetrope.swing.SwingComponentFactory
Code Sample 29-2 — Including a Component Factory
The example adds one of the component factories shipped with XUI. 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. Setup the registration factory First the factory needs to be registered with the main class, this is done through the startup.properties file.
NumComponentFactories=2 ComponentFactory0=net.xoetrope.swing.SwingComponentFactory ComponentFactory1=net.xoetrope.optional.registry.XRegisteredComponentFactory
Code Sample 29-3 — Adding the XRegisteredComponentFactory
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.
ComponentRegistry=components.xml
Code Sample 29-4 — Specifying the Component Registry
The components file then details each component for example:
Code Sample 29-5 — Using the New 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
• • • • 312 • • Carousel, User Guide 29 – Working with components
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.
Table 29-1 — Registering Swing components via reflection.
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
Table 29-2 — Using reflection to set a component property
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.
• • • • 313 • • 29 – Working with components Carousel, User Guide
Anatomy of a ComponentFactory
Every component factory must implement the XComponentConstructor interface. This interface is relatively simple and is discussed below in the context of the source code for the SwingComponentFactory class.
1 package net.xoetrope.swing; 2 3 import net.xoetrope.xui.XComponentConstructor; 4 import net.xoetrope.xui.XComponentFactory; 5 import java.awt.Component; 6 import java.util.Hashtable;
7 8 /** 9 * A factory for non-base Swing components such as Trees 10 *
Copyright: Copyright (c) Xoetrope Ltd., 2001-2004
11 *$Revision: 1.3 $
12 * License see license.txt 13 */ 14 public class SwingComponentFactory implements XComponentConstructor 16 { 17 private String packageName = "net.xoetrope.swing"; 18 18 public SwingComponentFactory() 20 { 21 // Register the extra binding factory 22 SwingDataBindingFactory.register(); 23 } 2425 /** 26 * A generic factory for adding XComponents. The component is constructed, positioned and 27 * added to the parent panel if one exists. The component is named with a counter value 28 * to uniquely identify the control. 29 * This factory does not use this method and all components must be added by name 30 * When a ScrollPane is addd it becomes the parent. 31 * @param cf the calling component factory 32 * @param type a constant identifying the type of component to be created 33 * @param content the component text/content 34 */ 35 public Component constructComponent( XComponentFactory cf, int type, String content ) 36 { 37 return null; 38 } 39
40 /** 41 * A generic factory for adding XComponents. The component is constructed, positioned and 42 * added to the parent panel if one exists. The component is named with a counter value 43 * to uniquely identify the control. 44 * @param cf the calling component factory 45 * @param type a name identifying the type of component to be created 46 * @param content the component text/content 47 */ 48 public Component constructComponent( XComponentFactory cf, String type, String content ) 49 { 50 Component comp = null; 51 if ( type.charAt( 0 ) == 'X' ) 52 type = type.substring( 1, type.length() );53 54 if ( type.compareToIgnoreCase( "Tree" ) == 0 ) 55 comp = new XTree(); 56 else if ( type.compareToIgnoreCase( "Table2" ) == 0 ) 57 comp = new XTable2(); 58 59 return comp; 60 }
Code Sample 29-6 — A Component Factory Implementation
• • • 314 • • • Carousel, User Guide 29 – Working with components
61 62 /** 63 * A generic factory method for adding non component elements. 64 * @param cf the calling component factory 65 * @param type the object type 66 * @param name a name identifying the element to be created 67 * @param content the component text/content 68 * @param attribs the element attributes if any 69 */ 70 public Object addElement( XComponentFactory cf, String type, String name, String content, Hashtable attribs ) 71 { 72 return null; 73 }
74 75 /** 76 * Notify the component factories that some of their settings may have changed. 77 * This factory does not yet use any startup properties or parameters 78 */ 79 public void update() {} 80 81 /** 82 * Set the package name for the factory's widgets. 83 */ 84 public void setPackageName( String defPackage ) 85 { 86 packageName = defPackage; 87 } 88 }
Code Sample 29-6 — A Component Factory Implementation
: .
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 XUI 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.
Code Sample 29-7 — Overview of the Component Factory Code
• • • • 315 • • 29 – Working with components Carousel, User Guide
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 Carousel'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.
Code Sample 29-7 — Overview of the Component Factory Code
Code Sample 29-8 — Using the New Components
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 XRegisteredComponentFactory 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 Carousel we have included the source for a test component in the KALIDEOSCOPE source distribution at test.xoetrope.components.TestShape. The component doesn’t do very much except paint a few trivial shapes. Here’s the source code:
package test.xoetrope.components;
import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JComponent;
import net.xoetrope.xui.XAttributedComponent;
/** * Draws a simple shape *
Copyright: Copyright Xoetrope Ltd. (c) 2001-2005
*License: see license.txt
*$Revision: 1.1 $
*/ public class TestShape extends JComponent implements XAttributedComponent { protected int shape = 0;Code Sample 29-9 — TestShape a sample component
• • • 316 • • • Carousel, User Guide 29 – Working with components
public static final int RECTANGLE = 0; public static final int ORTHO_LINE = 1; public static final int EXTRABOLD_HORIZONTAL = 2; public static final int BOLD_HORIZONTAL = 3; public static final int NORMAL_HORIZONTAL = 4; public static final int THIN_HORIZONTAL = 5; public static final int EXTRABOLD_VERTICAL = 6;
public static final int BOLD_VERTICAL = 7; public static final int NORMAL_VERTICAL = 8; public static final int THIN_VERTICAL = 9; public static final int RIGHT_TOP_LINE = 10; public static final int LEFT_TOP_LINE = 11; public static final int ELLIPSE = 12; public static final int SOLID_ELLIPSE = 13; public static final int SOLID_DIAMOND = 14; public static final int DIAMOND = 15;
/** * Constructor for a new XShape */ public TestShape() { }
/** * Fills the shape with the background color * @param g the graphics context */ public void paintComponent( Graphics g ) { super.paintComponent( g );
Dimension d = getSize(); g.setColor( getForeground());
switch( shape ) { case RECTANGLE: g.fillRect( 0, 0, d.width, d.height ); break; case ORTHO_LINE: g.fillRect( 0, 0, getSize().width, getSize().height ); break;
case EXTRABOLD_HORIZONTAL: g.drawLine( 0, 0 +3, d.width, 3 ); case BOLD_HORIZONTAL: g.drawLine( 0, 0 +2, d.width, 2 ); case NORMAL_HORIZONTAL: g.drawLine( 0, 0 +1, d.width, 1 ); case THIN_HORIZONTAL: g.drawLine( 0, 0, d.width, 0 ); break;
case EXTRABOLD_VERTICAL: g.drawLine( 3, 0, 3, d.height ); case BOLD_VERTICAL: g.drawLine( 2, 0, 2, d.height ); case NORMAL_VERTICAL: g.drawLine( 1, 0, 1, d.height ); case THIN_VERTICAL: g.drawLine( 0, 0, 0, d.height ); break;
Code Sample 29-9 — TestShape a sample component
• • • • 317 • • 29 – Working with components Carousel, User Guide
case EXTRABOLD_VERTICAL: g.drawLine( 3, 0, 3, d.height ); case BOLD_VERTICAL: g.drawLine( 2, 0, 2, d.height ); case NORMAL_VERTICAL: g.drawLine( 1, 0, 1, d.height ); case THIN_VERTICAL: g.drawLine( 0, 0, 0, d.height ); break;
case RIGHT_TOP_LINE: g.drawLine( d.width, 0, 0, d.height ); break; case LEFT_TOP_LINE: g.drawLine( 0, 0, d.width, d.height ); break;
case ELLIPSE: g.drawOval( 0, 0, d.width, d.height ); break; case SOLID_ELLIPSE: g.fillOval( 0, 0, d.width, d.height ); break;
case SOLID_DIAMOND: case DIAMOND: { int[] xpts = new int[ 4 ]; int[] ypts = new int[ 4 ]; xpts[ 0 ] = 0 + d.width / 2; xpts[ 1 ] = 0 + d.width; xpts[ 2 ] = 0 + d.width / 2; xpts[ 3 ] = 0; ypts[ 0 ] = 0; ypts[ 1 ] = 0 + d.height / 2; ypts[ 2 ] = 0 + d.height; ypts[ 3 ] = 0 + d.height / 2; if ( shape == SOLID_DIAMOND ) g.fillPolygon( xpts, ypts, 4 ); else g.drawPolygon( xpts, ypts, 4 ); } break;
} }
/** * Get shape identifier. The ID is an enumerated constant * @param shapeId the new shape ID. */ public void setShape( int shapeId ) { shape = shapeId; repaint(); }
/** * Get shape identifier. * @return the enumerated constant for this shape */ public int getShape() { return shape; }
Code Sample 29-9 — TestShape a sample component
• • • 318 • • • Carousel, User Guide 29 – Working with components
/** * Set one or more attributes of the component. Currently this handles the * attributes: *
- *
- shape, value=1 to 15 *
repaint( 100 ); } }
Code Sample 29-9 — TestShape a sample component
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 Carousel.
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 Carousel. Using the component registry editor we can add the file and select the Shape property for inclusion. This process generates the following XML:
Code Sample 29-10 — The XML registration for the test component.
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 XUI code, XUI XML and normal Java and to illustrate this ability to extend the framework lets look at how to add a web browser component.
• • • • 319 • • 29 – Working with components Carousel, User Guide
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 XDialog class and therefore we need to subclass XDialog:
package net.xoetrope.mypackage;
import java.net.URL;
import java.awt.BorderLayout; import java.awt.Panel;
import org.jdesktop.jdic.browser.WebBrowser; import net.xoetrope.swing.XDialog; import net.xoetrope.xui.data.XBaseModel; import net.xoetrope.xui.data.XModel; import javax.swing.SwingUtilities; import org.jdesktop.jdic.browser.WebBrowserListener; import org.jdesktop.jdic.browser.WebBrowserEvent; import java.net.URLConnection;
/** * Show more information about a component *
Copyright Xoetrope Ltd. (c) 2003-2004
*License: see license.txt
* $Revision: 1.3 $ not attributable */ public class XBrowserDialog extends XDialog implements WebBrowserListener { XModel selectedNode; int nodeNumber; XDialog webDialog; WebBrowser webBrowser;public XBrowserDialog() { }
public void pageActivated() { SwingUtilities.invokeLater( new Runnable() { public void run() { website(); } } ); }
public void done() { if ( wasMouseClicked() ) { stop(); closeDlg(); } }
/** * Show the website */ public void website() { try { // Check if the net is accessible URL url = new URL( "http://www.xoetrope.com" ); URLConnection conn = url.openConnection(); if ( conn != null ) { conn.connect();
Code Sample 29-11 — Embed a web browser.
• • • 320 • • • Carousel, User Guide 29 – Working with components
String websiteUrl = ( String ) getAttribute( "url", "websiteBtn" ); webDialog = new XDialog( false, 0 ); webDialog.setCaption( "Internet content" ); webDialog.setModal( true );
//Use below code to check the status of the navigation process, //or register a listener for the notification events. webBrowser = new WebBrowser(); webBrowser.setDebug( true ); webBrowser.addWebBrowserListener( this ); WebBrowser.Status myStatus = webBrowser.getStatus();
try { webBrowser.setURL( new URL( websiteUrl ) ); } catch ( Exception e ) { System.out.println( e.getMessage() ); return; }
Panel panel = new Panel(); panel.setLayout( new BorderLayout() ); panel.setLocation( 0, 0 ); panel.setSize( 700, 500 ); panel.add( webBrowser, BorderLayout.CENTER );
webDialog.setLayout( new BorderLayout() ); webDialog.getContentPane().add( panel, BorderLayout.CENTER ); webDialog.setSize( 700, 500 ); webDialog.doLayout(); webDialog.showDialog( parent ); closeDlg(); } } catch ( Exception ex ) { } }
private void stop() { if ( webBrowser != null ) { webBrowser.stop(); webBrowser.setVisible( false ); webBrowser = null; } }
// Methods from WebBrowserListener public void downloadStarted(WebBrowserEvent webBrowserEvent) {}
public void downloadCompleted(WebBrowserEvent webBrowserEvent) { // webDialog.hide(); }
public void downloadProgress(WebBrowserEvent webBrowserEvent) {}
public void downloadError(WebBrowserEvent webBrowserEvent) {}
public void titleChange(WebBrowserEvent webBrowserEvent) {}
public void statusTextChange(WebBrowserEvent webBrowserEvent) {} }
Code Sample 29-11 — Embed a web browser.
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
• • • • 321 • • 29 – Working with components Carousel, User Guide
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 Carousel 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:
private static boolean splashActivated = false;
public void pageActivated() { // This flag prevents the splash screen being redisplayed if the first page is reactivated // or shown again. if ( !splashActivated ) { showVersionInfoDialog( true ); splashActivated = true; } }
Code Sample 29-12 — Launch a splash screen
• • • 322 • • • Carousel, User Guide 29 – Working with components
private XDialog showVersionInfoDialog( boolean modal ) { final XDialog popupDialog = (XDialog)pageMgr.loadPage( "SplashScreen" ); final XPage thisPage = this; final boolean isModal = modal;
SwingUtilities.invokeLater( new Runnable() { public void run() { // Only needed if the content contains bound controls. popupDialog.updateBindings();
popupDialog.setModal( isModal );
// Force the dialog to calculate its size popupDialog.pack(); popupDialog.setSaveOnClose( false );
// Sleep for 5 seconds and then close the dialog - only for the ‘modeless’ version. // Change or remove the next if statement if you want a model splash screen. // to be dismissed on the timer if ( !isModal ) { new Thread() { public void run() { long startTime = new Date().getTime(); try { // Loop so that the dialog can repaint itself once any images have loaded while (( new Date().getTime() - startTime ) < 5000L ) { popupDialog.repaint(); Thread.currentThread().sleep( 100 ); } } catch ( Exception ex ) {} popupDialog.closeDlg(); } }.start(); } popupDialog.showDialog( thisPage, “Welcome to my application”, null );
} } ); return popupDialog; }
Code Sample 29-12 — Launch a splash screen
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 XDialog and as such it needs to derived from the XDialog class instead of XPage. If you are using XML don’t forget to specify the class in the page declaration as follows:
Code Sample 29-13 — Specify the XDialog class for your splash screen in XML
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 Carousel application. And remember, where XML is insufficient you can supplement it with Java.
• • • • 323 • • 29 – Working with components Carousel, User Guide
Swing splash screen
In the Swing version of the XUI 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 XUI 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 XSplashWindow class is invoked and it in turn starts the class passed as an argument (or XApplet if none is specified), which should be the entry point to the XUI framework (which, of course depending on the application style being used). The the application is invoked as follows:
java -cp ./lib/XuiCore.jar;.lib/XuiCoreSwing.jar net.xoetrope.swing.splash.XSplashWindow
Table 29-3 — Starting with a splash screen
where
Figure 29-3 — A sample docking layout.
• • • 324 • • • Carousel, User Guide 29 – Working with components
The results really depend on what you add as the splash image. The splash screen can also be customized by subclassing the XSplashWindow class, and in the image below this has been done to add transparency support
Figure 29-4 — A transparent image.
Component customization
Carousel 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:
Table 29-4 — Setting atable customizer
• • • • 325 • • 29 – Working with components Carousel, User Guide
the customizer attribute refers to a customization specified in the customizations.xml file. The customization file is as follows:
Table 29-5 — A sample table customization
The result of the above customization is a table like the following with row striping and customized headers:
Figure 29-5 — A customized table.
TODO further describe the customizations and how they translate to method calls
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.
Table 29-6 — Drag and drop configuration
• • • 326 • • • Carousel, User Guide 29 – Working with components
Table 29-6 — Drag and drop configuration
TODO add an example
• • • • 327 • • 29 – Working with components Carousel, User Guide
• • • 328 • • • 30 – Using SVG graphics Carousel, User Guide
30 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 XUI and Carousel. 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. Carousel 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.
Choosing how to install components
Carousel 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 Carousel 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
XSvgImageMap A panel class that can display SVG images. XSvgRolloverFinder An interface containing methods that can be implemented to aid the setup of rollover blocks. XSvgMagnifyingWindow An SVG magnifing component that can display a magnified portion of an SVG image. XSvgMagnifyingGlass An SVG magnifing component that can display a magnified portion of an SVG image in a magnifying glass.
• • 329 • • • • 30 – Using SVG graphics Carousel, User Guide
XSvgMagnifier The XSvgMagnifier class uses the XSvgMagnifyingGlass and XSvgMagnifyingWindow classes to magnify an SVG image. XDisplaySvg A JComponent class that can display SVG images. XGrabMap Used to grab and drag an SVG image using the XSvgImageMap class. XGroupHierarchy Extracts the SVG group hierarchy from an SVG image. XMapArea Defines an area's bounds based on a set of points. XOverlayComponent A non-opaque component, in which other components can be added, that can be used as an overlay. XPointSystem Converts points from the User coordinate system to the SVG coordinate system and vice versa. XPopUpWindow A pop-up window that can be used to display textual information. XRoutePlotter Used to plot a route within an SVG image using a series of waypoints. XSelectSvgArea Used to select a region within an svg image an return all elements within that region. XSvgLayer A layer, which elements can be added to, that can be added to an svg image. XViewBoxListener Listens for changes to the svg image and repositions registered components accordingly. XViewBoxModifier Used to modify the viewbox size and position. XWaypoint A waypoint representing a point within an SVG image. XWaypointFlag A flag that can be added to an SVG image to mark a waypoint.
• • • 330 • • • Carousel, User Guide 30 – Using SVG graphics
XInfoFileParser Used to parse an xml file and pass the information to a class implmenting the XStatusInfo interface. XPrinterInfo A component which implments the XStatusInfo interface and displays printer info parsed from an xml file. XHotSpotInfo An interface that can be implemented by any class that needs to have data passed to it from XInfoFileParser class. XRolloverComponent A class that can display a component when a rollover event occurs.
• • • • 331 • • Carousel, User Guide 30 – Using SVG graphics
• • • • 332 • • 31 – Printing and export Carousel, User Guide
31 Printing and export
[This is a feature of Carousel] Printouts can be produced in a number of different ways, directly and indirectly through applications like Excel and OpenOffice. Carousel 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
Carousel’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. Carousel 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 Carousel this should involve little additional work while at the same time adding versatility to the printout infrastructure. The printing infrastructure Printing in Carousel uses the normal Java printing API and mechanisms, but in order to hide some of the low level details Carousel provides some additional infrastructure. The printing infrastructure revolves around three classes in the com.xoetrope.print package. These classes are:
Printout This class encapsulates the print job. Control of headers and footers is also provided.
PrintablePage A decoration of the XPage 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.
Code Sample 31-1 — The Printing Classes
These classes act as decorations of the XPage 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.
• • 333 • • • • 31 – Printing and export Carousel, User Guide
The use of these classes are as follows:
/** * On a mouse click print this page */ public void printPage() { // Hide the navigation buttons for the home page if ( wasMouseClicked() ) { Printout po = new Printout(); po.showPageFormat(); po.setJobName( "Survey Printout" ); po.setHeader( "Left Header", Printout.LEFT ); po.setHeader( "Right Header", Printout.RIGHT ); po.setFooter( "Footer", Printout.LEFT ); po.setPageNumbers( false, Printout.RIGHT, "Page " );
int numFrames = pageManager.getNumTargets(); PrintableFrameSet pf = new PrintableFrameSet( po ); for ( int i = 0; i < numFrames; i++ ) pf.addFrame( pageManager.getTarget( i ) ); po.addFrame( pf );
po.print(); } }
Code Sample 31-2 — Printing a Frameset
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 Carousel 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.
• • • 334 • • • Carousel, User Guide 31 – Printing and export
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 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 Carousel 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.
Code Sample 31-3 — Printing Content Issues
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. Carousel 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
• • • • 335 • • Carousel, User Guide 31 – Printing and export
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:
Figure 31-1 — Configuring the output
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.
private void createExporter( String fileName ) throws Exception { if ( textRb.getState()) exporter = new XExportHelper(); else if ( wordRb.getState()) exporter = new XExportHelper( ".doc" ); else if ( xmlRb.getState() ) exporter = new XmlExportHelper(); else if ( htmlRb.getState() ) exporter = new HtmlExportHelper(); else if ( excelRb.getState() ) exporter = new ExcelExportHelper( fileName ); }
Table 31-1 — Setup the export infrastructure
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::
private void exportText( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory );
String delim = delimiterEdit.getText(); exporter.setLeftDelimiter( delim ); exporter.setRightDelimiter( delim ); delim = separatorEdit.getText();
Table 31-2 — Setup the export format details
• • • • 336 • • Carousel, User Guide 31 – Printing and export
exporter.setFieldSeparator( delim ); exporter.setFieldNameSeparator( delim ); exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport();
exporter.close(); } catch ( IOException ex ) { } }
private void exportXml( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory );
exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport();
exporter.close(); } catch ( IOException ex ) { } }
private void exportHtml( Writer w ) { try { exporter.setOutputWriter( w ); exporter.setComponentFactory( pageHelper.componentFactory );
exporter.setUseWindowsLineEnd( windowsCheck.getState()); doExport();
exporter.close(); } catch ( IOException ex ) { } }
private void exportExcel( String fileName ) { try { exporter = new ExcelExportHelper( fileName ); exporter.setComponentFactory( pageHelper.componentFactory );
exporter.setUseWindowsLineEnd( windowsCheck.getState() ); doExport();
exporter.close(); } catch ( IOException ex ) { ex.printStackTrace(); } }
Table 31-2 — Setup the export format details
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.
• • • • 337 • • 31 – Printing and export Carousel, User Guide
The actual export is initiated in the BeginExport method:
public void beginExport() { String targetFile = locationEdit.getText(); int type = TEXT; try { if ( wasMouseClicked() ) { if ( !clipboardCheck.getState() && ( targetFile.length() == 0 ) ) { showMessage( "Unable to Export", "You must enter a file name or click browse to choose a file!" ); return; }
root = XModel.getInstance();
Writer w = null; createExporter( targetFile );
// Choose the output, either the clipboard or an output stream (file) if ( !clipboardCheck.getState() && !excelRb.getState() ) w = exporter.setupWriter( this, targetFile ); else w = new StringWriter();
if ( w != null ) { // Start the export process if ( textRb.getState() ) exportText( w ); else if ( xmlRb.getState() ) { exportXml( w ); type = XML; } else if ( htmlRb.getState() ) { exportHtml( w ); type = HTML; } else if ( excelRb.getState() ) { exportExcel( locationEdit.getText() ); type = EXCEL; } else if ( wordRb.getState() ) { exportText( w ); type = WORD; }
if ( clipboardCheck.getState() ) { exporter.setClipboardContents( ( ( StringWriter ) w ).getBuffer().toString() ); } }
if ( exportRb.getState() || exportAndOpenRb.getState()) { showMessage( "Export complete", "Your file can be found at " + targetFile ); if ( exportAndOpenRb.getState()) doOpen( targetFile, false, type ); } else if ( exportAndPrintRb.getState() ) { showMessage( "Export complete", "Your file has been printed and can be found at " + targetFile ); doOpen( locationEdit.getText(), true, type ); } } } catch ( Exception ex ) { ex.printStackTrace(); showMessage( "Export failed", "Unable to export to " + targetFile ); } }
Table 31-3 — Start the export process
• • • 338 • • • Carousel, User Guide 31 – Printing and export
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 or print a file * @param fileName the file name * @param doPrint true to print */ private void doOpen( String fileName, boolean doPrint, int fileType ) { // This method is all over the palce and needs to be refactored! String command = "start"; try { File tempFile = null; String tempFileName = null;
if ( doPrint ) { switch ( fileType ) { case TEXT: command = "NOTEPAD /P" + "\"" + fileName + "\""; break;
case XML: case HTML: case EXCEL: case WORD: { if (( tempPrintFileName == null ) || ( fileType != lastFileType )) { tempFile = File.createTempFile( "doPrint", ".bat" ); tempPrintFileName = tempFile.getCanonicalPath(); lastFileType = fileType; tempOpenFileName = null; } tempFileName = tempPrintFileName;
if ( tempFile != null ) { OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) ); writer.write( getOfficePath( fileType, doPrint ) + " %1 /mFilePrintDefault" ); writer.flush(); writer.close(); } command = "start \"" + tempFileName + "\" \"" + fileName + "\""; ; } break; } }
else { if (( tempOpenFileName == null ) || ( lastFileType != fileType )) { tempFile = File.createTempFile( "doOpen", ".bat" ); tempOpenFileName = tempFile.getCanonicalPath(); lastFileType = fileType; tempPrintFileName = null; } tempFileName = tempOpenFileName;
if ( tempFile != null ) { OutputStreamWriter writer = new OutputStreamWriter( project.getBufferedOutputStream( tempFileName, false ) ); writer.write( "start %1 %2 %3 %4 %5 %6 %7 %8 %9" ); writer.flush(); writer.close(); } }
Table 31-4 — Open the output file
• • • • 339 • • 31 – Printing and export Carousel, User Guide
String line; String cmdLine; if ( doPrint ) cmdLine = command; else cmdLine = "start \"" + tempFileName + "\" \"" + fileName + "\"";
File targetFile = new File( fileName ); File startDir = new File( targetFile.getParent() ); if ( BuildProperties.DEBUG ) DebugLogger.log( "Running: " + cmdLine );
String osName = System.getProperty( "os.name" ); String[] cmd = new String[ 3 ]; if ( osName.equals( "Windows 95" ) || osName.equals( "Windows 98" ) ) { cmd[ 0 ] = "command.com"; cmd[ 1 ] = "/C"; cmd[ 2 ] = cmdLine; } else { cmd[ 0 ] = "cmd.exe"; cmd[ 1 ] = "/C"; cmd[ 2 ] = cmdLine; }
Process p = Runtime.getRuntime().exec( cmd ); BufferedReader input = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
while ( ( line = input.readLine() ) != null ) { if ( BuildProperties.DEBUG ) System.out.println( line ); } input.close(); } catch ( IOException ex ) { ex.printStackTrace(); } }
Table 31-4 — Open the output file
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:
private String getOfficePath( int fileType, boolean doPrint ) { try { String applicationQuery = null; switch ( fileType ) {
case EXCEL: applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\EXCEL.EXE\\shell\\edit\\command"; break;
case HTML: if ( doPrint ) applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command"; else applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\iexplore.exe\\shell\\edit\\command"; break;
• • • 340 • • • Carousel, User Guide 31 – Printing and export
case TEXT: case WORD: default: applicationQuery = REGQUERY_UTIL + "\"HKCR\\Applications\\WINWORD.EXE\\shell\\edit\\command"; break; }
Process process = Runtime.getRuntime().exec( applicationQuery ); StreamReader reader = new StreamReader( process.getInputStream() );
reader.start(); process.waitFor(); reader.join();
String result = reader.getResult(); int p = result.indexOf( REGSTR_TOKEN );
if ( p == -1 ) return null;
result = result.substring( p + REGSTR_TOKEN.length() ).trim(); result = result.substring( 0, result.indexOf( "\"", 1 ) + 1 ).trim(); return result; } catch ( Exception e ) { return null; } }
Ultimately, to generate the document we must ouput each part of the document
private void doExport() throws IOException { exporter.startDocument(); exportPartsList(); exporter.endDocument(); }
Table 31-5 — Run through each step of the document
And finally the elements within that document section must be output.
private void exportPartsList() throws IOException { exporter.writeSectionTitle( "Components" ); exporter.setOutputFieldNames( false ); exporter.startTable();
exporter.startHeader(); exporter.writeField( "type", "" ); exporter.writeField( "description", "" ); exporter.writeField( "codeNo", "" ); exporter.writeField( "quantity", "" ); exporter.endHeader();
int itemId = 0; int numNodes = getNumNodes(); for ( int i = 0; i < numNodes; i++ ) { XModel targetNode = getNode( i ); if ( targetNode != null ) {
exporter.startGroup(); exporter.startRecord(); exporter.startElement( "component" ); exporter.writeField( "type", targetNode.getAttribValueAsString( XBaseModel.VALUE_ATTRIBUTE )); exporter.writeField( "description", pageHelper.componentFactory.translate( (String)targetNode.get( "@Description" ) ));
Table 31-6 — Run through each step of the document
• • • • 341 • • 31 – Printing and export Carousel, User Guide
exporter.writeField( "codeNo", ( String ) selectedNode.get( "@Code_no" )); exporter.writeField( "quantity", "1" ); exporter.endRecord();
int accessories = 0; for ( int ij = 0; ij < accessoryList.length; ij++ ) { Object accessoryCode = selectedNode.get( "@" + accessoryList[ ij ] ); if ( accessoryCode != null ) { exporter.startRecord();
if ( accessories == 0 ) exporter.endElement(); exporter.startElement( "accessory" ); exporter.writeField( "type", "" ); exporter.writeField( "description", pageHelper.componentFactory.translate( Functional.accessoryDescription[ ij ] )); exporter.writeField( "codeNo", (String)accessoryCode ); exporter.writeField( "quantity", new Integer( Functional.accessoryQuantity[ ij ] ).toString() ); accessories++; exporter.closeElement(); exporter.endRecord(); } } if ( accessories == 0 ) exporter.closeElement(); else exporter.matchElement(); exporter.endGroup(); } } }
}
exporter.endTable(); exporter.setOutputFieldNames( true ); exporter.writeSectionEnd(); exporter.writeBlankLine(); }
Table 31-6 — Run through each step of the document
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 Carousel 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. Carousel also includes classes to allow you easily launch Excel to preview the ‘printout’ prior to actual printing.
• • • 342 • • • Carousel, User Guide 31 – Printing and export
Using the code above we can get the following Excel sheet (displayed in OpenOffice 2.0):
Figure 31-2 — Export to Excel or OpenOffice
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. Carousel 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.
• • • • 343 • • 31 – Printing and export Carousel, User Guide
Again using the code above we can get the following HTML, shown in the Mozilla FireFox browser:
Figure 31-3 — The exported HTML output in FireFox
• • • 344 • • • 32 – Drag and drop Carousel, User Guide
32 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.
Table 32-1 — Drag and drop setup
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 XTransferHandlerFactory in somewhere in your application (typically the constructor of the first page):
public Welcome() { new XTransferHandlerFactory( project ); }
Table 32-2 — Make the application drag and drop enabled
Customizing drag and drop
Drag and drop functionality can be implemented within a page by having that page implement the XDragManager interface. The interface provides a getDragInfo method for supplying info to the drop target from the dragged component.
• • 345 • • • • 32 – Drag and drop Carousel, User Guide
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:
Figure 32-1 — A sample drag and drop shopping cart
A code example of the implementation of the interface and the getDragInfo method is shown below.
public class Catalog extends XPage implements XDragManager { protected HashMap infoMap;
public Catalog() { infoMap = new HashMap(); String [] keys = { XDragInfo.TITLE, XDragInfo.KEY, "price", "image", XDragInfo.DESCRIPTION };
Object [][] v = { {"Canon Digital Camera", "camera", "$249.99", "Camera.jpg", "The Canon PowerShot SD550 is a sleek 7 megapixel ultracompact." }, { "Netgear Network Storage Device", "netstore", "$99.99", "networkstorage.jpg", "NETGEAR’s Storage Central is an innovation for storing and protecting files." }, { "Netgear Wireless Card", "wireless", "$39.99", "wirelesscard.jpg", "With the RangeMax NEXT Notebook adapter inserted, your PC will maintain a steady, constant network connection." }};
Table 32-3 — Setting up drag and drop data
• • • 346 • • • Carousel, User Guide 32 – Drag and drop
for (int i = 0; i < v.length; i++) { XDragInfo dragInfo = new XDragInfo(); dragInfo.addValues( keys, v[ i ] ); infoMap.put( (String)v[i][1], dragInfo ); } }
public XDragInfo getDragInfo( String key ) { return (XDragInfo)infoMap.get( key ); } }
Table 32-3 — Setting up drag and drop data
Each instance of the XDragInfo class stores information for a draggable component. When the component is dropped on the target, the XModelTransferHandler class uses the components dragInfo property to query the hash map and extract the XDragInfo object assigned to the component. An example of setting the dragInfo property on a component is shown below.
Table 32-4 — Enabling the components
The XModelTransferHandler's createTransferable method is responsible for extracting the relevant XDragInfo instance from the class implementing the XDragManager interface and then creating a new transferable. The transferable is then passed to the drop target using the importData method of the XModelTransferHandler class. The drag info is then extracted from the transferable and passed to the drop component. Drag and drop and data bindings
Drag and drop and components
Extending drag and drop
Registering new flavors
Drag enabling new components
• • • • 347 • • Carousel, User Guide 32 – Drag and drop
• • • • 348 • • 33 – Importing HTML Carousel, User Guide
33 Importing HTML
Carousel allows HTML files to be opened and saved as XUI xml files. This import facility provides a means of working with existing web applications and enabling an upgrade to XUI and Carousel. One size doesn’t fit all and while Carousel and XUI 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. Carousel and XUI 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, Carousel has the capacityto import HTML pages and save them as XUI XML files. Once imported the pages can be manipulates as any other XUI page.
• • 349 • • • • 33 – Importing HTML Carousel, User Guide
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 XUI 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 XUI by adding a BuilderClass property to the startup properties file:
BuilderClass=com.xoetrope.builder.w3c.html.HtmlBuilder
NumBuilderClasses=2 BuilderClass0=net.xoetrope.builder.XuiBuilder BuilderClass1=com.xoetrope.builder.w3c.html.HtmlBuilder
Table 33-1 — Setting up the 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.
• • • 350 • • • Carousel, User Guide 33 – Importing HTML
• • • • 351 • • Carousel, User Guide 33 – Importing HTML
• • • • 352 • • 34 – Importing forms Carousel, User Guide
34 Importing forms
New in Carousel 3.0 Carousel 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 Carousel and XUI’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.
• • 353 • • • • 34 – Importing forms Carousel, User Guide
Opening HTML within the IDE
• • • 354 • • • Carousel, User Guide 34 – Importing forms
• • • • 355 • • Carousel, User Guide 34 – Importing forms
• • • • 356 • • 35 – Packaging and deployment Carousel, User Guide
35 Packaging and deployment
This chapter will describe the steps involved in packaging and deploying a Carousel 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 Carousel project all of the project files can be merged into a single jar.
# Create the file with a mainfest file c:\j2sdk1.4.2_03\bin\jar cvf bundle.jar META-INF/MANIFEST.MF # navigate to the resources directory and jar it's contents. # Repeat this for all resource files cd resources c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.* cd ..\pages c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.* cd ..\images c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.* cd ..\classes c:\j2sdk1.4.2_03\bin\jar uvf ../bundle.jar *.* # Extract the contents of the jars which need to be included # in the application into a tmep directory and jar them. cd ..\lib rd temp md temp cd temp ########## STARTED EXTRACTING JARS ############ c:\j2sdk1.4.2_03\bin\jar xvf ../XuiCoreSwing.jar c:\j2sdk1.4.2_03\bin\jar xvf ../XuiOptional.jar ########## FINISHED EXTRACTING JARS ########### c:\j2sdk1.4.2_03\bin\jar uvf ../../bundle.jar *.* cd..\..
Code Sample 35-1 — Sample jar script
The first line in this script references a file called META-INF/MANIFEST.MF which looks like...
Manifest-Version: 1.0 Main-Class: net.xoetrope.swing.XApplet
Code Sample 35-2 — The manifest file
• • 357 • • • • 35 – Packaging and deployment Carousel, User Guide
This file simply tells the jar that the net.xoetrope.swing.XApplet class needs to be loaded when the jar file is run. Note that the startup file is not specified in which case the XApplet 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.
• • • 358 • • • Carousel, User Guide 35 – Packaging and deployment
Running a Carousel 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 Carousel project folder, the application can be launched using the jar which was created in the previous section
java.exe -cp bundle.jar net.xoetrope.swing.XApplet startup.properties
Code Sample 35-3 — Launch Carousel with the command line
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.
java -cp .;.\lib\XuiCoreSwing.jar;.\lib\XuiOptional.jar;.\lib\Carousel.jar;.\bundle.jar; net.xoetrope.swing.XApplet startup.properties
Code Sample 35-4 — Specify the jars separately
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.
# cd ..\lib # rd temp # md temp # cd temp ########## STARTED EXTRACTING JARS ############ # c:\j2sdk1.4.2_03\bin\jar xvf ../XuiCoreSwing.jar # c:\j2sdk1.4.2_03\bin\jar xvf ../XuiOptional.jar ########## FINISHED EXTRACTING JARS ########### # c:\j2sdk1.4.2_03\bin\jar uvf ../../bundle.jar *.*
Code Sample 35-5 — Lines to be remove from the bundle.bat file
Running a Carousel 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.
Company URL
Verisign http://www.verisign.com/products-services/security-services/code-signing/ digital-ids-code-signing/
Code Sample 35-6 — Sources for code signing certificates
• • • • 359 • • Carousel, User Guide 35 – Packaging and deployment
Company URL
Thawte http://www.thawte.com/codesign/
GlobalSign http://www.globalsign.net/digital_certificate/objectsign/index.cfm
Code Sample 35-6 — Sources for code signing certificates
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.
c:\jdk1.5.0\bin\keytool -genkey -alias my_keystore -keypass mykeypassword -keyalg RSA -keystore mycompany.keystore -storepass mystorepassword
Table 35-1 — Generate a new keystore for you certificates
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:
c:\jdk1.5.0\bin\keytool -certreq -alias my_keystore -keystore mycompany.keystore -file certreq.txt
Table 35-2 — Generate a certificate signing request
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:
c:\jdk1.5.0\bin\keytool -alias my_keystore -keypass mykeypassword -keystore mycompany.keystore - storepass mystorepassword -import -file cert.txt -trustcacerts
Table 35-3 — Import the new certificate
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.
jarsigner -keystore mycompany.keystore -signedjar bundleS.jar bundle.jar
Code Sample 35-7 — Signing the jar file
• • • • 360 • • Carousel, User Guide 35 – Packaging and deployment
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...
Code Sample 35-8 — A sample JNLP file
In this example the JNLP file can be invoked by referencing http:// www.mydomain.com/demos/carousel.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 XUI jars are also listed, but you could just include everything in the one jar file. However, leaving the XUI 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 XUI 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.
Resource URL
JWS Developer’s guide
Signing tools
Java deployment guide
JWS examples
NetBeans JWS plug-in http://www.netbeans.org/kb/articles/matisse-jaws.html
JNLP visual editor http://public.wuapaa.com/public/NetBeans/
Table 35-4 — Additional Java Web Start documentation
• • • • 361 • • 35 – Packaging and deployment Carousel, User Guide
Setting up a JWS extension If you have more than one application based upon Carousel 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).
Code Sample 35-9 — A sample JNLP file
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.
• • • 362 • • • Carousel, User Guide 35 – Packaging and deployment
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):
@echo off SET DRIVE=%0 SET DRIVE=%DRIVE:~1,1% c:\jdk1.5.0\bin\javaws -codebase file:///%DRIVE%:/myapp/ -import %DRIVE%:/myapp/myApp.jnlp
Table 35-5 — Import the jars into the JWS cache
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.
Running a Carousel 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.
Code Sample 35-10 — Applet tag for the applet
It is possible to start an application written with the Swing packages as an applet by using the object tag
Code Sample 35-11 — Starting a swing application from a browser
Creating a setup for your Carousel application
There are a number of ways of deploying your Carousel 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.
• • • • 363 • • 35 – Packaging and deployment Carousel, User Guide
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 Carousel project root directory. Within this directory the following izPack configuration xml file can be created.
Code Sample 35-12 — The izPack configuration file
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.
• • • 364 • • • Carousel, User Guide 35 – Packaging and deployment
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:
"c:\Programmer\izpack\bin\compile" install.xml -b . -o install.jar -k standard
Code Sample 35-13 — Create the installation jar file
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.
# Global entries, can be overriden by specific ones. jar = install.jar download = http://www.java.com/
# Win32 specific entries [win32] jre = jre/setup.exe
Code Sample 35-14 — The modified Launcher.ini file
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.
• • • • 365 • • 35 – Packaging and deployment Carousel, User Guide
• • • 366 • • • 36 – Compile, build and test Carousel, User Guide
36 Compile, build and test
Java applications must be compiled prior to executing and Carousel applications are no exception to this rule. Carousel uses Java code for response methods of all sorts so almost all Carousel applications will require some compilation. Fortunately this is rarely a complicated process. Testing is an important consideration for any application, compiled or not and Carousel includes easy to use tools to assist in testing.
Debugging
XUI 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
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
Code Sample 36-1 — Logging Levels
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 XUI 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 XUI framework will then output a summary of the logging information to the console. You should check this information for the presence of any errors
• • 367 • • • • 36 – Compile, build and test Carousel, User Guide
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.
# Turn debug zones on/off at design time TRANSLATION=off RESOURCES=off
Table 36-1 — Sample DebugZones.properties file
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 XUI 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 XUI project of interest in the Projects window and choosing the Debug option. If your project is the default project you
• • • 368 • • • Carousel, User Guide 36 – Compile, build and test
can just click on the debug button on the toolbar or choose the Debug Main Project option from the Run menu
Figure 36-1 — Start debugging in NetBeans from the project or the toolbar.
You may also want to add the source code for the XUI platform to the project to allow you step into the XUI 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
• • • • 369 • • 36 – Compile, build and test Carousel, User Guide
window in the bottom right contains much information about inherited (from XPage) variables:
Figure 36-2 — NetBeans at a breakpoint showing information about the page.
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
Carousel libraries can be used with commercial IDEs such as JBuilder. Configuring JBuilder for use with Carousel is a simple process.
• • • 370 • • • Carousel, User Guide 36 – Compile, build and test
Under the project properties add a new library entry for Carousel and point it at the Carousel class files and resources.
Figure 36-3 — Setup a new library in JBuilder
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.
Figure 36-4 — Choose an existing library in JBuilder
• • • • 371 • • 36 – Compile, build and test Carousel, User Guide
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.
Figure 36-5 — The JBuilder project run dialog
• • • 372 • • • Carousel, User Guide 36 – Compile, build and test
Automating testing
TestPilot is a module included in Carousel 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.
Figure 36-6 — Setting up the integrated TestPilot testing tool.
Carousel 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.
• • • • 373 • • 36 – Compile, build and test Carousel, User Guide
• • • 374 • • • 37 – Troubleshooting Carousel, User Guide
37 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 XUI 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.
• • 375 • • • • 37 – Troubleshooting Carousel, User Guide
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 XUI 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 XUI 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.
• • • 376 • • • Carousel, User Guide 37 – Troubleshooting
Check the XML is properly formed Carousel relies heavily on XML and while this is very useful it can provide a source for numerous problems. Carousel itself and XUI 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 Carousel’s built-in editors you should be able to use Carousel 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 “Setting the system look and feel” on page 137 I get an exception when using Synth
The expressions in the xsynth file are incorrect The .xsynth 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: Uncaught error fetching image: java.lang.NullPointerException at sun.awt.image.URLImageSource.getConnection(URLImageSource.java:97) at sun.awt.image.URLImageSource.getDecoder(URLImageSource.java:106) at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:240) at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172) at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)
• • • • 377 • • 37 – Troubleshooting Carousel, User Guide
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.
• • • 378 • • • SECTION IV REFERENCE 37 – Carousel, User Guide
• • • 380 • • • 38 – Carousel UI Components Carousel, User Guide
38 Carousel UI Components
Carousel adds many user interface (UI) components to the XUI framework, while some of these components are non-visual components, the extensions offered by Carousel can greatly improve the overall visual impact of your application. This section documents some of the Carousel specific components. The components fall into a number of categories although some components are related.
Graphical Components
XShape Class: com.xoetrope.swing.XShape 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 Carousel’s flowed text components to nest other objects that would otherwise have a rectangular area.
• • 381 • • • • 38 – Carousel UI Components Carousel, User Guide
In the example below an image component is embedded in an ellipse shape, and the shape then interacts with a XFlowedText component to alter the text layout. The shape may also be defined programmatically.
Figure 38-1 — An ellipse shape with an embedded image The XML for the above example is shown below:
Table 38-1 — Nesting an image within a shape
• • • 382 • • • Carousel, User Guide 38 – Carousel UI Components
The component is also used to draw some simple adornments like lines using the component foreground and background colors.
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
Table 38-2 — Shape drawing modes
• • • • 383 • • 38 – Carousel UI Components Carousel, User Guide
XEasterEgg Class: com.xoetrope.swing.XEasterEgg 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.
Figure 38-2 — The password dialog
Under the hood the XEasterEgg component is an extension of the XImage component, so normally it will display an image and uses the image controls normal attributes. The component relies on the com.xoetrope.event.XClickListener 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.
XReflectedImage
The reflected image component displays an image and a reflection of that image. For example:
Figure 38-3 — A reflected image of a computer
• • • 384 • • • Carousel, User Guide 38 – Carousel UI Components
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
XCaptionedImage
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.
Figure 38-4 — A captioned image
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
• • • • 385 • • 38 – Carousel UI Components Carousel, User Guide
Attribute Description 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
Text Components
As you may have already noticed the components provided by Carousel generally work in concert with one another. The text components interact with one another and with components like the XShape 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 XUI application is pretty easy. The text components presented here form a hierarchy and all use such a mechanism and are easily extended and customized. XFlowedTextComponent Class: com.xoetrope.swing.XFlowedTextComponent 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 Carousel’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
• • • 386 • • • Carousel, User Guide 38 – Carousel UI Components
some other layout that can support overlapping components. Carousel’s GuideLayout can do just this.
Figure 38-5 — Text flowing around sibling controls
The layout for the above page is defined as follows:
Table 38-3 — XML for a flowed text component
• • • • 387 • • 38 – Carousel UI Components Carousel, User Guide
Table 38-3 — XML for a flowed text component
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
XHtmlText Class: com.xoetrope.swing.XHtmlText For compatibility reasons some of the text components in Carousel 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.
• • • 388 • • • Carousel, User Guide 38 – Carousel UI Components
XPolygonalTextArea Class: com.xoetrope.swing.XPolygonalTextArea Using this component you can flow text within an arbitrary polygon. The component, like the XFlowedTextArea clips any siblings from its area. The component is ideal for flowing text around things like images.
Figure 38-6 — Text flowing around siblings and within a defined polygonal area Similar to the example above for the FlowedTextComponent the layout for the PolygonalTextArea can be defined as follows:
Table 38-4 —
• • • • 389 • • 38 – Carousel UI Components Carousel, User Guide
Table 38-4 —
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
• • • 390 • • • Carousel, User Guide 38 – Carousel UI Components
XReflectedText Class: com.xoetrope.swing.XReflectedText 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.
Figure 38-7 — Reflected text 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
• • • • 391 • • 38 – Carousel UI Components Carousel, User Guide
XRotatedText Class: com.xoetrope.swing.XRotatedText Text can be rotated to any angle using this component.as illustrated below
Figure 38-8 — Rotated text flowed through two columns 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
• • • 392 • • • Carousel, User Guide 38 – Carousel UI Components
Attribute Description
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
XAnimatedText Class: com.xoetrope.swing.XAnimatedText 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
• • • • 393 • • 38 – Carousel UI Components Carousel, User Guide
XCreditsText Class: com.xoetrope.swing.XCreditsText 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.
Figure 38-9 — Credits text fading out as it reaches the top of its client area 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
• • • 394 • • • Carousel, User Guide 38 – Carousel UI Components
Attribute Description
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.
XMarqueeText Class: com.xoetrope.swing.XMarqueeText 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
XBaseTable Class: com.xoetrope.swing.XBaseTable An extension of the basic table wrappers in the XUI framework. This table component adds some additional features, including... The component is due to be consolidated into the XUI XTable component in due course.
• • • • 395 • • 38 – Carousel UI Components Carousel, User Guide
XCheckList Class: com.xoetrope.swing.XCheckList 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.
Figure 38-10 — A checked list - useful when lots of options need to be selected
• • • 396 • • • Carousel, User Guide 38 – Carousel UI Components
XDateEdit Class: com.xoetrope.swing.XDateEdit 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.
Figure 38-11 — The date Chooser
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...
XKeypad Class: com.xoetrope.swing.XKeypad 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.
• • • • 397 • • 38 – Carousel UI Components Carousel, User Guide
The attributes are:
Attribute Description
style The basic style
selected3d The style for the 3d elements
styleShade The style for the shaded border
XMoneyEdit Class: com.xoetrope.swing.XMoneyEdit An edit field for monetary values. More details to follow. XSpinner Class: com.xoetrope.swing.XSpinner 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
XSlider Class: com.xoetrope.swing.XSlider 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
• • • 398 • • • Carousel, User Guide 38 – Carousel UI Components
XProgress Class: com.xoetrope.swing.XProgress 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
Data Components
XGraph Class: com.xoetrope.swing.XGraph 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
• • • • 399 • • 38 – Carousel UI Components Carousel, User Guide
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.
Table 38-5 — A query to select some sample data
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:
Table 38-6 — Adding a graph to a 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.
protected void setupChart( boolean animate, boolean outputTable ) { XModel root = XProjectManager.getModel(); XModel chartData = (XModel)root.get( "Outcome" ); if ( chartData != null ) { chart.setTitle( "Opportunities by Outcome" ); chart.setXScale( -45, 20, 0, "Outcome", null ); chart.setYScale( chart.LINEAR, "Value" ); chart.setModelStructure( chart.SERIES, 1 ); chart.setModelStructure( chart.LABELS, 0 ); chart.setModelStructure( chart.VALUES, 2 ); chart.setModel( chartData ); }
Table 38-7 — Setting up the chart data
• • • 400 • • • Carousel, User Guide 38 – Carousel UI Components
else { // Alternatively setup some dummy data for DEMO purposes numSeries = 5; int numPoints = 14; int seriesPts = numPoints * 2 * numSeries; capacityPoints = new double[ seriesPts ];
double te[] = { -45.0, -40.0, -35.0, -30.0, -25.0, -20.0, -15.0, -10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0 }; double ts[] = { 0.9, 1.0, 1.1, 2.0, 2.1 };
int idx = 0; for ( int j = 0; j < numSeries; j++ ) { for ( int i = 0; i < numPoints; i++ ) { capacityPoints[ idx++ ] = te[ i ]; capacityPoints[ idx++ ] = te[ i ] * te[ i ] * ts[ j ]; }
} showChart( 1, seriesNames, animate ); } }
Table 38-7 — Setting up the chart data
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.
• • • • 401 • • 38 – Carousel UI Components Carousel, User Guide
The data for the sample charts can be seen below:
Figure 38-12 — Sample chart data
• • • 402 • • • Carousel, User Guide 38 – Carousel UI Components
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..
Figure 38-13 — A scatter plot with multiple data series. 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.
• • • • 403 • • 38 – Carousel UI Components Carousel, User Guide
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.
Figure 38-14 — Line chart with multiple data series. 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
• • • 404 • • • Carousel, User Guide 38 – Carousel UI Components
to make this chart really useful, but reducing the number of series displayed would make the chart a lot clearer.
Figure 38-15 — Bar chart with multiple data series.
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
• • • • 405 • • 38 – Carousel UI Components Carousel, User Guide
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
Figure 38-16 — Stacked bar chart with multiple data series.
• • • 406 • • • Carousel, User Guide 38 – Carousel UI Components
Finally pie charts. The pie charts are slightly different to other chart types in that they only display a single series
Figure 38-17 — Pie chart with a single data 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. XTreeTable Class: com.xoetrope.swing.XTreeTable
• • • • 407 • • 38 – Carousel UI Components Carousel, User Guide
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.
Figure 38-18 — A TreeTable component showing Carousel component attributes 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.
• • • 408 • • • Carousel, User Guide 38 – Carousel UI Components
Multimedia Components
XAudio Class: com.xoetrope.swing.XAudio 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. XVideo Class: com.xoetrope.swing.XVideo 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 otehrwise there may be a lot of empty space.
Figure 38-19 — A sample logon screen with a video clip running in the background.
• • • • 409 • • 38 – Carousel UI Components Carousel, User Guide
The above screen can be created very easily, here’s the XML:
Table 38-8 — Creating a video backed logon screen.
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
• • • 410 • • • SECTION IV CASE STUDIES 38 – Carousel, User Guide
• • • 412 • • • 1 – Carousel, User Guide
Case Study 1 — Mortgage application
Introduction
The Mortgage application which is outlined as part of this case study is taken from the tutorial which is available for download from the XUI project on the sourceforge site. The intention of the tutorial is the make the developer familiar with development elements of the XUI/ Carousel framework. The development of the application is split over several different tutorials which gives the developer a fully working version of the application at the end of each step. In this case study we are going to examine the elements which make up the application in each tutorial. In summary the tutorials are as follows
Introduction This step shows how to set up the basic structure of a XUI application and what files and libraries need to be included. It creates some simple pages and shows how to navigate the application. It shows how to add page events, validations, data bindings and dynamic bindings. The application is completely localized in the final step and the model is used to output captured data. Advanced This step builds upon the introductory tutorial and provides the user with a more generic navigation which allows new pages to be added very easily. It also shows how library functions can be used to help with the navigation. Custom validations are created which are loaded via a custom XValidationFactory. Framesets are customised and component registration is used to develop and load custom components. Custom dialogs are created which report on the types of errors being generated. Model data is saved from and restored to the model showing how easy it is to create applications where projects can be created and opened.
KALIDEOSCOPE This tutorial really replicates what was done in the first step except that the development is carried out with the help of the KALIDEOSCOPE editor.
Table 1-1 — The tutorials and what they teach
• • 413 • • • • 1 – Carousel, User Guide
Carousel In introducing Carousel the application is makes use of the routes which are available in the Carousel library. This allows the developer to create applications which can easily connect to a servlet server making use of authentication and session management services. Data can be cached on the client and the application can be used to work with them offline and then synchronise with the server. Custom ServiceProxy classes are created which are used in transforming mode data.
Table 1-1 — The tutorials and what they teach
Introduction tutorial
This tutorial begins by showing the project structure for a typical XUI application as shown in the diagram below.
Figure 1-1 — A typical XUI project structure
The run.bat file contains the text below.
java -cp .;lib\XuiCoreSwing.jar;images;pages;resources;build\classes net.xoetrope.swing.XApplet
Code Sample 1-1 — The run.bat file
The application is made up of frames which are defined in the frames.xml file as shown below.
Code Sample 1-2 — The frames.xml file
• • • 414 • • • Carousel, User Guide 1 –
Navigation The project navigation was handled by the navPanel page in the navPanel frame. In order to do this two images where added to the navPanel page as shown below
Code Sample 1-3 — navPanel.xml page
Two images are added and each has a MouseHandler event added to them. The nextPage and prevPage functions are defined in the NavPanel class as follows.
package net.xoetrope.mortgage;
import net.xoetrope.xui.*;
public class NavPanel extends XPage {
public void nextPage() { if ( wasMouseClicked()) navigateToPage( "next" ); }
protected void navigateToPage( String key ) { XPage target = ( XPage )( ( XTarget ) pageMgr.getTarget( "content" ) ).getComponent( 0 ); String dest = ( String )target.getAttribute( key, null ); if ( dest != null ) pageMgr.showPage( dest, "content" ); }
public void prevPage() { if ( wasMouseClicked() ) pageMgr.showPrevious(); } }
Code Sample 1-4 — The NavPanel class
This navigation depended on the presence of the next attribute in the main content page
• • • • 415 • • Carousel, User Guide 1 –
Bindings Dynamic binding was used to address different nodes within the data model. On the personal page the binding are declared as follows.
...
Code Sample 1-5 — The bindings on the personal page
These bindings depend on the callback function getCustomerID in the Personal class as follows.
public String getCustomerID() { return "customer" + ( String )currentCust.get(); }
Code Sample 1-6 — The callback function in the Personal page
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.
public class Finish extends XPage { public void pageActivated() { try { String path = System.getProperty( "user.dir" ) + File.separator + "save.xml"; FileOutputStream fos = new FileOutputStream( path ); OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" ); BufferedWriter bw = new BufferedWriter( osw ); XDataSource.outputModel( bw, ( ( XModel ) rootModel.get( "xui_state/mortapp" ) ) ); bw.flush(); bw.close(); } catch (Exception e) { System.out.println( "error" ); }
} }
Code Sample 1-7 — The Finish class
• • • • 416 • • Carousel, User Guide 1 –
This provided us with the following output.
<
Code Sample 1-8 — Model data saved from the application
Localization The entire application was localized through the use of language specific resource bundles. For example the personal page was localized simply by changing the content attributes for the components.
Code Sample 1-9 — The localized personal page
The resources were entered into two resource bundles for English and French.
WEL_LANG=Language WEL_TEXT=Welcome to ABC Bank's mortgage application. Before we proceed please note the following... WEL_SOLE=Sole WEL_JOINT=Joint BAN_TITLE=ABC Bank Mortgage Application BAN_CLOSE=Close Application PER_HEADING_1=Personal Details (First Applicant) PER_HEADING_2=Personal Details (Second Applicant) ...
Code Sample 1-10 — Extract from en.properties
• • • • 417 • • 1 – Carousel, User Guide
And the French translation:
WEL_LANG=Langue WEL_TEXT=Bienvenue ? la demande de pr?t hypoth?caire de l'hypoth?que de la banque de ABC. Avant que nous veuillez proc?der note le suivant... WEL_SOLE=Unique WEL_JOINT=Joint BAN_TITLE=Demande de pr?t hypoth?caire D'Hypoth?que De Banque de ABC BAN_CLOSE=Application ?troite PER_HEADING_1=D?tails Personnels (Premier Demandeur) PER_HEADING_2=D?tails Personnels (Deuxi?me Demandeur) ...
Code Sample 1-11 — Extract from fr.properties
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.
public void changeLanguage() { if ( langClicked ) { String lang = ( String )languageList.getSelectedObject(); pageMgr.reset(); String langCode = lang.compareTo( "English" ) == 0 ? "en" : "fr"; project.setStartupParam( "Language", langCode ); pageMgr.loadFrames( "frames", true ); } }
Code Sample 1-12 — Language switching code
This completed the introductory tutorial and the resulting application can be seen below
Figure 1-2 — The localized welcome page
• • • 418 • • • Carousel, User Guide 1 –
Advanced Tutorial
This tutorial uses the application created in the introductory tutorial and begins by adding custom validations and a custom validation factory. The custom validation calls a function within the XPage and returns the result of it. The custom validation is shown below.
public class FunctionValidation extends XBaseValidator {
public FunctionValidation() { }
public void validate( Object c, boolean forceMandatory ) throws Exception { Integer ret = ( Integer ) invokeMethod(); if ( ret.intValue() > LEVEL_IGNORE ) { errorLevel = ret.intValue(); throwException(); } }
public void setup( XmlElement element ) { message = element.getAttribute( "msg" ); super.setup( element ); } }
Code Sample 1-13 — The custom FunctionValidation factory
In order to use this validation a custom validation factory need to be created as shown below.
public class ValidationFactory extends XValidationFactory {
...
public XValidator getValidation( String validationName, Method m, int mask, Object page ) { XmlElement ele = ( XmlElement ) validations.get( validationName );
String type = ele.getAttribute( "type" );
if ( type.compareTo( "function" ) == 0 ) { FunctionValidation validator = new FunctionValidation(); validator.setName( validationName ); validator.setValidationMethod( m, page ); validator.setup( ele ); return validator; } else { XBaseValidator validator = ( XBaseValidator ) super.getValidation( validationName, mask, page ); validator.setup( ele ); return validator; } }
}
Code Sample 1-14 — The custom ValidationFactory class
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.
• • • • 419 • • 1 – Carousel, User Guide
In order for XUI to use this validation factory the following line needed to be added to the startup properties.
ValidationFactory=net.xoetrope.mortgage.validation.ValidationFactory
Code Sample 1-15 — Startup property for the custom validation factory
Now the welcome page was amended to use the new validation.
Code Sample 1-16 — XML declaration for the function validation
And the welcome class was amended to include the checkCreateSetup function
... public Integer checkCreateSetup() { int level = XBaseValidator.LEVEL_IGNORE; if ( !jointRadio.isSelected() && !soleRadio.isSelected() ) level = XBaseValidator.LEVEL_ERROR;
return new Integer( level ); } ...
Code Sample 1-17 — The checkCreateSetup function
This function checks to see that at least one of the radio buttons on the welcome page has been selected. If everything is OK then LEVEL_IGNORE is returned otherwise LEVEL_ERROR is returned and the error message is displayed. Bindings The tutorial now goes on to show how the data model can be reopened once saved. This required the use of a custom data binding which was added to a list component on the welcome page.
... ...
Code Sample 1-18 — Binding to the list component on the welcome page
• • • 420 • • • Carousel, User Guide 1 –
Now the saved files needed to be listed when the pageCreated function was called.
public void pageCreated() { ... loadFiles(); } ... private void loadFiles() { String dir = System.getProperty( "user.dir" ); appsMdl = ( XBaseModel )rootModel.get( "applications" ); File folder = new File( dir + "\\files" ); File files[] = folder.listFiles(); for ( int i = 0; i < files.length; i++ ) { String name = files[ i ].getName(); XBaseModel filMdl = new XBaseModel ( appsMdl, name, name ); } updateBindings(); } ...
Code Sample 1-19 — Loading files into the model
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.
... public void openFile() { String filename = ( String )fileList.getSelectedObject();
// Not shown here but simply reads the file into a string. String contents = getFileContents( filename ); restoreState( contents );
updateBindings(); }
private void restoreState( String contents ) { ( ( XBaseModel )rootModel.get( "xui_state/mortapp" ) ).clear(); ( ( XBaseModel )rootModel.get( "mortapp" ) ).clear();
StringReader sr = new StringReader( contents ); XmlElement ele = XmlSource.read( sr );
if ( ele != null ) { XOptionalDataSource ds = new XOptionalDataSource(); ds.loadTable( ele, rootModel ); ds.loadTable( ele, ( ( XBaseModel )rootModel.get( "xui_state" ) ) ); }
} ...
Code Sample 1-20 — Opening and restoring the model state
The restoreState function is really of most importance here. It initialises the xui_state and mortapp nodes. It then loads the model into both of these. Again, updateBindings is called so as to refresh the state of the radio buttons on the welcome page.
• • • • 421 • • 1 – Carousel, User Guide
The list can be seen on the welcome page below.
Figure 1-3 — The bound list on the welcome page
Navigation The navigation of the applicaton was improved by making use of the library functions which are part of XUI. To do this the navPanel page was changed to reference functions in the Navigator class as follows.
Code Sample 1-21 — The amended MouseHandler events in the navPanel page
The Navigator page was created as a Singleton instance class and included the static functions next and previous. The pages to be included in the navigation were defined in a new navigation dataset as follows.
Code Sample 1-22 — The navigation.xml file
• • • 422 • • • Carousel, User Guide 1 –
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.
public class CustomerPage extends XPage { public String getCustomerID() { return "customer" + Navigator.getInstance().getCurrentCust(); }
}
Code Sample 1-23 — The generic CustomerPage class
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.
Figure 1-4 — New page added to the navigation
• • • • 423 • • 1 – Carousel, User Guide
Framesets and component registration The framesets in were amended to include a status frame to show the user exactly where they were in the application navigation. A new frame declaration was added to the frames.xml file as follows.
Code Sample 1-24 — The new declaration in frames.xml
This frame was made invisible on the welcome page and visible on subsequent pages. It’s visibilty was controlled by the Navigation class. A custom component was then created which listed all of the pages to be processed and highlighted the current one. It was declared in the components.xml file as follows.
Code Sample 1-25 — The components.xml file with the declaration for NavViewer
Now a NavViewer instance was added to the navPanel page as follows.
Code Sample 1-26 — The AppStatus.xml file
• • • 424 • • • Carousel, User Guide 1 –
Now the NavViewer class could take care of displaying navigation information as below.
Figure 1-5 — The status frame with the embedded custom component
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.
public boolean handleException( Object comp, Exception ex, Object xvalidator ) { ... if ( validator.getLevel() == validator.LEVEL_ERROR ) errors.add( currentPage.translate( msg ) ); else warnings.add( currentPage.translate( msg ) );
return true; }
Code Sample 1-27 — The ExceptionHandler class
• • • • 425 • • 1 – Carousel, User Guide
public int accumulateMessages( boolean start, int level ) { pageValidation = start; if ( pageValidation ){ errors = new Vector(); warnings = new Vector(); } else { if ( errors.size() > 0 || warnings.size() > 0 ) return getDialogResult( level ); } return level; }
private int getDialogResult( int level ) { ErrorMessage dlg = (ErrorMessage)XProjectManager.getPageManager().loadPage( "ErrorDlg", false ); dlg.setErrorMessages( errors, warnings ); int result = dlg.showDialog( currentPage ); if ( level == XValidator.LEVEL_WARNING ) return result == XValidator.LEVEL_IGNORE ? XValidator.LEVEL_IGNORE : level; else return XValidator.LEVEL_ERROR; }
Code Sample 1-27 — The ExceptionHandler class
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.
... public int showDialog( Container owner ) { super.showDialog( owner ); return ret; }
public void cancelClicked() { ret = XBaseValidator.LEVEL_WARNING; super.closeDlg(); }
public void okClicked() { ret = XBaseValidator.LEVEL_IGNORE; super.closeDlg(); } ...
Code Sample 1-28 — The ErrorMessage class
• • • 426 • • • 2 – Carousel, User Guide
Case Study 2 — 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 Xoetrope’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 Carousel including page navigation, framesets, component registration, localization, validations, the synth look and feel and data binding amongst others.
Figure 2-1 — The Wizard welcome page
• • 427 • • • • 2 – Carousel, User Guide
Framesets and pages
The application consists of a XUI 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 XPage. 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.
Code Sample 2-1 — frames.xml
From the welcome page, it is possible to select one of four radio buttons which will determine the route through the wizard.
...
Code Sample 2-2 — The four radio buttons and their declared events
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.
... XRadioButton createRad, generateRad, buildRad, hotspotRad; XLabel infoLbl;
Code Sample 2-3 — The Welcome class
• • • 428 • • • Carousel, User Guide 2 –
public void pageCreated() { createRad = ( XRadioButton )findComponent( "createRad" ); generateRad = ( XRadioButton )findComponent( "generateRad" ); buildRad = ( XRadioButton )findComponent( "buildRad" ); hotspotRad = ( XRadioButton )findComponent( "hotspotRad" ); infoLbl = ( XLabel ) findComponent( "infoLbl" ); ... }
public void showInfo() { NavPanel navPanel = ( NavPanel ) pageMgr.getPage( "navPanel" ); String text = ""; if ( createRad.isSelected() ) { text = "WEL_CREATE_TEXT"; navPanel.setNavPath( "wizard" ); } else if ( generateRad.isSelected() ) { text = "WEL_GEN_TEXT"; navPanel.setNavPath( "generator" ); } else if ( buildRad.isSelected() ) { text = "WEL_PACK_TEXT"; navPanel.setNavPath( "builder" ); } else if ( hotspotRad.isSelected() ) { text = "WEL_HOTSPOT_GEN"; navPanel.setNavPath( "hotspot" ); } infoLbl.setText( Translator.translate( text ) ); itemSelected = true; } ...
Code Sample 2-3 — The Welcome class
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 XPage 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.
• • • • 429 • • Carousel, User Guide 2 –
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.
Figure 2-2 — ProjectInit page with the status page on the left
The NavPanel class controls the navigation of the wizard and is shown below.
public class NavPanel extends BasePage {
XButton prevBtn, nextBtn; BasePage currentPage; NavigationManager wizardNavigator, generatorNavigator, builderNavigator, hotspotNavigator, currentNavigator;
public void pageCreated() { prevBtn = ( XButton ) findComponent( "prevBtn" ); nextBtn = ( XButton ) findComponent( "nextBtn" ); setCursors(); wizardNavigator = new WizardNavigator( nextBtn, prevBtn, "navigation/wizardpages" ); generatorNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/generationpages" ); builderNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/builderpages" ); hotspotNavigator = new NavigationManager( nextBtn, prevBtn, "navigation/hotspotpages" ); currentNavigator = wizardNavigator; }
Code Sample 2-4 — The NavPanel class
• • • • 430 • • Carousel, User Guide 2 –
public void setNavPath( String name ) { if ( name.compareTo( "wizard" ) == 0 ) currentNavigator = wizardNavigator; else if ( name.compareTo( "generator" ) == 0 ) currentNavigator = generatorNavigator; else if ( name.compareTo( "builder" ) == 0 ) currentNavigator = builderNavigator; else if ( name.compareTo( "hotspot" ) == 0 ) currentNavigator = hotspotNavigator; }
private void setCursors() { prevBtn.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); nextBtn.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); }
public void prevPage() { currentNavigator.showPrevPage(); }
public void nextPage() { currentNavigator.showNextPage(); }
public void goHome() { currentNavigator.showHomePage(); }
public void home() { goHome(); } }
Code Sample 2-4 — The NavPanel class
The pages of this application are using a custom BasePage class which is derived from the XPage. The reasons for doing this are irrelevant to this discussion. The setNavPath function which is called from the Welcome XPage 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.
Code Sample 2-5 — wizardpages.xml
• • • • 431 • • 2 – Carousel, User Guide
Code Sample 2-5 — wizardpages.xml
Now the NavigationManager can take care of displaying the correct page depending on whether the next or previous button was clicked.
public class NavigationManager {
XBaseModel navMdl; protected int currentNode = 0; BasePage currentPage; XButton nextBtn, prevBtn; WizardStatus wizStatus; XPageManager pageMgr = XProjectManager.getPageManager();
public NavigationManager( XButton nextBtn, XButton prevBtn, String navPath ) { navMdl = ( XBaseModel ) XProjectManager.getModel().get( navPath ); this.nextBtn = nextBtn; this.prevBtn = prevBtn; wizStatus = ( WizardStatus )pageMgr.showPage( "wizardstatus", "wizardstatus" ); setWizardPanelSize( 0 ); }
public void showPrevPage() { nextBtn.setVisible(true); currentNode--; showpage(); if (currentNode == 0) prevBtn.setVisible(false); }
public void showNextPage() { if ( currentPage == null ) { XBaseModel mdl = (XBaseModel) navMdl.get( currentNode ); String pagename = (String) mdl.get(); currentPage = (BasePage) pageMgr.getPage( pagename ); wizStatus.setNavModel( navMdl ); } if ( currentPage.checkValidations() == XValidator.LEVEL_IGNORE ) { prevBtn.setVisible(true); currentNode++; showpage(); if (currentNode == ( navMdl.getNumChildren() - 1 ) ) nextBtn.setVisible(false); } }
Code Sample 2-6 — NavigationManager class
• • • 432 • • • Carousel, User Guide 2 –
public void showHomePage() { currentPage = null; currentNode = 0; nextBtn.setVisible( true ); prevBtn.setVisible( false ); XBaseModel mdl = (XBaseModel) navMdl.get( currentNode ); String pagename = (String) mdl.get(); currentPage = (BasePage) pageMgr.showPage( pagename ); setWizardPanelSize( 0 ); } private void showpage() { XBaseModel mdl = ( XBaseModel ) navMdl.get( currentNode ); String pagename = ( String ) mdl.get(); currentPage = ( BasePage ) pageMgr.showPage( pagename ); setWizardPanelSize( 100 ); wizStatus.setCurrentItem( currentNode ); } protected void setWizardPanelSize( int width ) { Container c = wizStatus.getParent(); c.setVisible( width == 0 ? false : true ); c.getParent().doLayout(); } }
Code Sample 2-6 — NavigationManager class
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.
public class WizardStatus extends BasePage {
int currentNode = -1; XBaseModel navMdl;
/** Creates a new instance of WizardStatus */ public WizardStatus() { }
public void setNavModel( XBaseModel mdl ) { navMdl = mdl; repaint(); }
Code Sample 2-7 — The WizardStatus class
• • • • 433 • • 2 – Carousel, User Guide
public void setCurrentItem( int item ) { currentNode = item; repaint(); }
public void paint( Graphics g ) { super.paint( g ); g.setColor( new Color( 136, 136, 136 ) ); if ( navMdl != null ) { int currentY = 50; for ( int i = 1; i < navMdl.getNumChildren(); i++ ) { if ( i == currentNode ) { g.setColor( new Color( 255, 58, 0 ) ); } else if ( i > currentNode ) { g.setColor( new Color( 136, 136, 0 ) ); } XBaseModel itemMdl = ( XBaseModel ) navMdl.get( i ); String text = Translator.translate( XModelHelper.getAttrib( itemMdl, "desc" ) ); g.drawString( text, 10, currentY ); currentY += 20; } } } }
Code Sample 2-7 — The WizardStatus class
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
• • • 434 • • • Carousel, User Guide 2 –
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.
public class FileLocater extends JPanel implements XAttributedComponent, ActionListener, XTextHolder { XStyleFactory fact; XLabel promptLbl; XEdit fileEdit; XButton locateBtn;
public FileLocater() { setLayout( null ); fact = new XStyleFactory( XProjectManager.getCurrentProject(), "net.xoetrope.swing" ); fact.setParentComponent( this ); promptLbl = ( XLabel ) fact.addComponent( "LABEL", 0, 0, 30, 20 ); fileEdit = ( XEdit ) fact.addComponent( "EDIT", 30, 0, 30, 20 ); locateBtn = ( XButton ) fact.addComponent( "BUTTON", 0, 0, 30, 20, "..." ); locateBtn.addActionListener( this ); }
public void setAttribute( String name, String value ) { if ( name.compareTo( "content" ) == 0 ) setContent( value ); else if ( name.compareTo( "promptWidth" ) == 0 ) setPromptWidth( value ); else if ( name.compareTo( "dataStyle" ) == 0 ) setDataStyle( value ); else if ( name.compareTo( "style" ) == 0 ) fact.applyStyle( promptLbl, value ); }
public void actionPerformed( ActionEvent ae ) { JFileChooser dlg = new JFileChooser( fileEdit.getText() ); dlg.setFileSelectionMode(javax.swing.JFileChooser.FILES_AND_DIRECTORIES); int returnVal = dlg.showOpenDialog( this );
if(returnVal == JFileChooser.APPROVE_OPTION) { fileEdit.setText( dlg.getSelectedFile().getAbsolutePath() ); } }
public void setContent( String value ) { promptLbl.setText( Translator.translate( value ) ); }
public void setPromptWidth( String value ) { promptLbl.setSize( Integer.parseInt( value ), promptLbl.getSize().width ); doLayout(); }
public void setDataStyle( String value ) { fact.applyStyle( fileEdit, value ); }
Code Sample 2-8 — FileLocator constructor, setAttribute and actionPerformed methods
The constructor sets the layout of the panel to null. It then constructs a new XStyleFactory which will use the net.xoetrope.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 XAttribedComponent interface is called from the XuiBuilder 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
• • • • 435 • • 2 – Carousel, User Guide
are set using the component functions. It can be seen how the XStyleFactory 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.
public void doLayout() { fileEdit.setLocation( promptLbl.getSize().width, 0 ); fileEdit.setSize( getWidth() - promptLbl.getWidth() - 30, 20 ); locateBtn.setLocation( getWidth() - 30, 0 ); setOpaque( false ); super.doLayout(); }
public String getText() { return fileEdit.getText(); }
public void setText( String text ) { fileEdit.setText( text == null ? "" : text ); }
public File getFile() { return new File( getText() ); }
public XEdit getEditComponent() { return fileEdit; }
public void setBackground( Color c ) { System.out.println( c ); super.setBackground( null ); super.setOpaque( false ); }
Code Sample 2-9 — Remainder of the FileLocater class
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.
Code Sample 2-10 — components.xml
• • • 436 • • • Carousel, User Guide 2 –
The following startup properties need to be set. The XRegisteredComponentFactory will assume a filename of components.xml
NumComponentFactories=2 ComponentFactory0=net.xoetrope.swing.SwingComponentFactory ComponentFactory1=net.xoetrope.registry.XRegisteredComponentFactory
Code Sample 2-11 — startup properties
The custom component is now ready to be used within the XUI 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.
Code Sample 2-12 — ProjectInit XML page declaration
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 XStyleManager 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 XUI core libraries. This requires the use of a custom validation factory which can be specified in the startup properties file as below.
... ValidationFactory=com.xoetrope.quotation.validation.ValidationFactory ...
Code Sample 2-13 — Specifying the custom validation factory
The custom validation factory is shown below.
public class ValidationFactory extends XValidationFactory {
public ValidationFactory() { super(); }
public ValidationFactory( Reader reader ) { super ( reader ); }
Code Sample 2-14 — The ValidationFactory class
• • • • 437 • • 2 – Carousel, User Guide
public XValidator getValidation( String validationName, Method m, int mask, Container page ) { XmlElement ele = ( XmlElement ) validations.get( validationName ); if ( BuildProperties.DEBUG ) { if ( ele == null ) { DebugLogger.logError( "Cannot find the validation rule: " + validationName ); return null; } }
String type = ele.getAttribute( "type" );
if ( type.compareTo( "integer" ) == 0 ) { IntegerValidation validator = new IntegerValidation(); validator.setName( validationName ); validator.setMask( mask ); validator.setup( ele ); return validator; } else if ( type.compareTo( "number" ) == 0 ) { NumberValidation validator = new NumberValidation(); validator.setName(validationName); validator.setMask(mask); validator.setup(ele); return validator; } else if ( type.compareTo( "function" ) == 0 ) { FunctionValidation validator = new FunctionValidation(); validator.setName( validationName ); validator.setup( ele ); return validator; } else { XBaseValidator validator = ( XBaseValidator ) super.getValidation( validationName, m, mask, page ); validator.setup( ele ); return validator; } }
}
Code Sample 2-14 — The ValidationFactory class
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:
public class NumberValidation extends XBaseValidator { public NumberValidation() { }
public void validate( Object c, boolean forceMandatory ) throws java.lang.Exception { if ( forceMandatory ) { String text = getText(c); try { Double.parseDouble( text ); } catch ( Exception ex ) { errorLevel = LEVEL_ERROR; throwException(); } } }
}
Code Sample 2-15 — The NumberValidation class
• • • 438 • • • Carousel, User Guide 2 –
And the second:
public class FunctionValidation extends XBaseValidator {
public FunctionValidation() { }
public void validate( Object c, boolean forceMandatory ) throws java.lang.Exception { Integer ret = ( Integer ) invokeMethod(); if ( ret.intValue() > LEVEL_IGNORE ) { errorLevel = ret.intValue(); throwException(); } }
public void setup( XmlElement element ) { String function = element.getAttribute( "method" ); super.setup( element ); }
}
Code Sample 2-16 — The FunctionValidation class
Both classes extend the XBaseValidator 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.
Code Sample 2-17 — Validations.xml
And the validation rules themselves are as follows:
Code Sample 2-18 — The validations defined in a page
The rate validation checks that the text entered into the taxEdit text component is numeric. The overwriteFile function calls the fileExists function in the XPage as shown below.
public Integer fileExists() { File f = new File( getPath() + "/xlintquote.xls" ); return new Integer( f.exists() ? XBaseValidator.LEVEL_WARNING : XBaseValidator.LEVEL_IGNORE ); }
Code Sample 2-19 — fileExists function
The fileExists function checks for the existence of a named file and if exists the XBaseValidator LEVEL_WARNING is returned in an Integer object. If it does not exist the LEVEL_IGNORE value is returned.
• • • • 439 • • 2 – Carousel, User Guide
One of the functions of the BasePage class is to set a custom ExceptionHandler for each of the pages as shown below.
public class BasePage extends XPage implements XModelListener {
public BasePage() { setExceptionHandler( new ExceptionHandler( this ) ); } ...
Code Sample 2-20 — BasePage constructor
The custom exception handler follows.
public class ExceptionHandler implements XExceptionHandler { boolean pageValidation = false; String validationText = ""; private XPage currentPage; Vector errors, warnings;
public ExceptionHandler( XPage page ) { currentPage = page; }
public boolean handleException( Object comp, Exception ex, Object xvalidator ) { XValidator validator = ( XValidator ) xvalidator; if ( ( validator.getLevel() == validator.LEVEL_ERROR ) && ( ! pageValidation ) ){ currentPage.showMessage( "Input error", ex.getMessage() ); return true; }
String msg = validator.getMessage(); if ( validationText.length()>0 ) validationText += "\n"; validationText += msg;
if ( validator.getLevel() == validator.LEVEL_ERROR ) errors.add( msg ); else warnings.add( msg );
return true; }
public int accumulateMessages( boolean start, int level ) { pageValidation = start; if ( pageValidation ){ errors = new Vector(); warnings = new Vector(); validationText = ""; }
else { if ( validationText.length()>0 ) { if ( level == XValidator.LEVEL_WARNING ) { ErrorMessage dlg = new ErrorMessage( errors, warnings ); dlg.setSaveOnClose( false ); int r = dlg.showDialog( currentPage ); return r == 0 ? 0 : level; }
Code Sample 2-21 — The ExceptionHandler class
• • • 440 • • • Carousel, User Guide 2 –
else { ErrorMessage dlg = new ErrorMessage( errors, warnings ); dlg.setSaveOnClose( false ); dlg.showDialog( currentPage ); return XBaseValidator.LEVEL_ERROR; } } } return level; } }
Code Sample 2-21 — The ExceptionHandler class
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 XValidator 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 XDialog 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.
...
Code Sample 2-22 — The FileLocator validations
These validations are declared in the validations file as below.
...
Code Sample 2-23 — The validations.xml declarations
• • • • 441 • • 2 – Carousel, User Guide
The projectPath is a simple built in mandatory validation and the functions for the FunctionValidations follows.
public Integer projectExists() { File f = pathEdit.getFile(); boolean ret = ( f.list() == null ) || ( f.list().length == 0 ); return new Integer( ret ? XBaseValidator.LEVEL_IGNORE : XBaseValidator.LEVEL_WARNING ); }
public Integer folderExists() { return new Integer( pathEdit.getFile().exists() ? XBaseValidator.LEVEL_IGNORE : XBaseValidator.LEVEL_ERROR ); }
public Integer projectFolder() { return new Integer( pathEdit.getFile().isDirectory() ? XBaseValidator.LEVEL_IGNORE : XBaseValidator.LEVEL_ERROR ); }
Code Sample 2-24 — The FunctionValidations functions
Data binding
The data bindings for the wizard application are on the whole pretty straightforward and there are some which deserve particular attention.
Code Sample 2-25 — The projectinit page
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.
• • • 442 • • • Carousel, User Guide 2 –
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.
public class FileLoaderBinding extends XTextBinding implements XCustomDataBinding { public FileLoaderBinding() { }
public void setup( Object c, XmlElement ele ) { comp = ( ( FileLocater ) c ).getEditComponent(); srcPath = ele.getAttribute( "source" ); XBaseModel srcModel = ( XBaseModel ) XProjectManager.getModel().get( srcPath ); outputPath = XModel.prefixOutputPath( srcPath ); if (( srcModel == null ) && ( srcPath != null )) sourceModel = ( XModel )XProjectManager.getModel().get( srcPath ); else sourceModel = srcModel; comp = c; attribStr = ele.getAttribute( "attrib" ); }
}
Code Sample 2-26 — FileLoaderBinding class
The custom binding extends the XTextBinding class and implements the XCustomDataBinding interface which specifies 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.
Figure 2-3 — Language setup page
• • • • 443 • • 2 – Carousel, User Guide
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.
Code Sample 2-27 — languages.xml
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.
XList masterLst, languageLst; LanguageModel masterListModel, listModel; XCheckbox defaultChk;
public void pageCreated() { langListMdl = ( XBaseModel ) rootMdl.get( "languagelist" );
languageLst = ( XList ) findComponent( "languageLst" ); masterLst = ( XList ) findComponent( "masterLst" ); defaultChk = ( XCheckbox ) findComponent( "defaultChk" );
listModel = new LanguageModel( "selectedLanguages" ); languageLst.setModel( listModel );
masterListModel = new LanguageModel(); masterLst.setModel( masterListModel );
populateMasterLangs(); populateDefaultLangs(); updateLists(); }
private void populateMasterLangs() { for ( int i = 0; i < langListMdl.getNumChildren(); i++ ) { XBaseModel langMdl = ( XBaseModel ) langListMdl.get( i ); String include = XModelHelper.getAttrib( langMdl, "include" ); if ( ( include == null ) || ( include.compareTo( "true" ) != 0 ) ) { masterListModel.addElement( langMdl ); } } }
Code Sample 2-28 — Some of the LanguagePage class
• • • 444 • • • Carousel, User Guide 2 –
private void populateDefaultLangs() { for ( int i = 0; i < langListMdl.getNumChildren(); i++ ) { XBaseModel langMdl = ( XBaseModel ) langListMdl.get( i ); String include = XModelHelper.getAttrib( langMdl, "include" ); if ( ( include != null ) && ( include.compareTo( "true" ) == 0 ) ) { listModel.addElement( langMdl ); } } }
private void updateLists() { masterLst.updateUI(); languageLst.updateUI(); }
Code Sample 2-28 — Some of the LanguagePage class
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.
public class LanguageModel implements ListModel {
XBaseModel langMdl;
public LanguageModel() { langMdl = new XBaseModel(); }
public LanguageModel( String path ) { langMdl = ( XBaseModel ) XProjectManager.getModel().get( path ); }
public void setDefault( String name ) { XModelHelper.setAttrib( langMdl, "default", name ); }
public String getDefault() { return XModelHelper.getAttrib( langMdl, "default" ); }
public void addElement( Object ele ) { langMdl.append( ( XBaseModel ) ele ); }
public void removeElement( Object ele ) { langMdl.remove( ( XBaseModel ) langMdl.get( ( ( Integer ) ele ).intValue() ) ); }
public XBaseModel getModelAt( int i ) { return ( XBaseModel )langMdl.get( i ); }
Code Sample 2-29 — The LanguageModel class
• • • • 445 • • 2 – Carousel, User Guide
public int getSize() { return langMdl.getNumChildren(); }
public Object getElementAt( int i ) { XBaseModel mdl = ( XBaseModel )langMdl.get( i ); String res = XModelHelper.getAttrib( mdl, "res" ); return Translator.translate( res ); }
public void addListDataListener( ListDataListener listener ){} public void removeListDataListener( ListDataListener listener ){} }
Code Sample 2-29 — The LanguageModel class
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.
Figure 2-4 — The wizard using Simplified Chinese as the selected language
• • • 446 • • • Carousel, User Guide 2 –
The default language for the wizard is English and that is specified in the startup properties as follows.
... Language=en ...
Code Sample 2-30 — The language startup property
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 XUI the resource attribute needs to be set in the page XML.
Code Sample 2-31 — The XPage resource attribute
... WIZ_PROJ_PROMPTS=Prompts PROMPT_DESC=Description\: DATA_LST_NAV_CFG=Configurator navigation DATA_LST_SCR_DRILL=Simple drilldown WEL_HOTSPOT_GEN=Create or edit a hotspot file COL_APP_TYPE=Application type\: PROJ_BUILD_LOC=Locate the spreadsheet you wish to generate WEL_CLICK_CONT=Click the next button to continue ...
Code Sample 2-32 — Section of the en.properties file
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.
...
Code Sample 2-33 — Some of the welcome.xml page
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
• • • • 447 • • 2 – Carousel, User Guide
attached to the language list changeClicked and changeLanguage and these are shown below.
... XComboBox langlist; boolean changeClicked = false;
public void changeClicked() { changeClicked = true; }
public void changeLanguage() { if ( changeClicked ) { String selected = (String) langlist.getSelectedObject(); XBaseModel mdl = (XBaseModel) rootModel.get( "wizardlanguages/" + selected ); String langCode = XModelHelper.getAttrib( mdl, "code" ); String langEncoding = XModelHelper.getAttrib(mdl, "encoding"); project.setStartupParam("Language", langCode); Translator.reset( langEncoding == null ? false : true ); pageMgr.reset(); pageMgr.showPage( "welcome", "content" ); pageMgr.showPage( "navPanel", "navPanel" ); pageMgr.showPage( "banner", "banner" ); } } ...
Code Sample 2-34 — Some of the WelcomePage class
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.
public class Translator {
protected static ResourceBundle languageResourceBundle; private static Translator translator; private static boolean UTF_Encoding = false; private static String defaultFont = "NSimSun";
private Translator() { XProject proj = XProjectManager.getCurrentProject(); languageResourceBundle = proj.getResourceBundle( proj.getStartupParam( "Language" ) ); }
public static Translator getInstance() { if ( translator == null ) translator = new Translator();
return translator; }
Code Sample 2-35 — The Translator Singleton class
• • • 448 • • • Carousel, User Guide 2 –
public static String translate( String key ) { if ( languageResourceBundle == null ) getInstance(); if (( key != null ) && ( languageResourceBundle != null )) { try { String trans = languageResourceBundle.getString( key ); if ( trans != null ) return trans; } catch ( Exception ex ) { if ( BuildProperties.DEBUG ) DebugLogger.logWarning( "No translation found for the key: " + key ); } } return key; }
public static void reset() { reset( false ); }
public static void setUseUTF( boolean use ) { UTF_Encoding = use; }
public static void reset( boolean useUTF ) { translator = null; getInstance(); translator.setUseUTF( useUTF ); UTF_Encoding = useUTF; }
public static boolean useUTF() { return UTF_Encoding; }
public static String getDefaultFont() { return defaultFont; }
}
Code Sample 2-35 — The Translator Singleton class
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 XStyleFactory in each of the XPages needs to make use of the Translator class in order to construct its
• • • • 449 • • 2 – Carousel, User Guide
components with the correct font so a new StyleManager class is introduced which extends the basic XStyleManager class as shown below.
public class StyleManager extends XStyleManager {
public Font getFont( String style ) { Font f = super.getFont( style ); if ( Translator.useUTF() ) { f = new Font( Translator.getDefaultFont(), f.getStyle(), f.getSize() ); } return f; }
public Font getFont( XStyle style ) { Font f = super.getFont( style ); if ( Translator.useUTF() ) { f = new Font( Translator.getDefaultFont(), f.getStyle(), f.getSize() ); } return f; }
}
Code Sample 2-36 — The StyleManager class
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 XUI to use this style manager which is done through the startup properties as follows.
... DefaultStylemanager=com.xoetrope.quotation.ui.StyleManager ...
Code Sample 2-37 — The DefaultStyleManager startup property
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.
... LAF=net.xoetrope.optional.laf.SynthLafInstaller SynthConfigFile=xui.xsynth SynthResourceLoader=com.xoetrope.controls.swing.Welcome ...
Code Sample 2-38 — Synth look and feel startup properties
• • • 450 • • • Carousel, User Guide 2 –
In order to use the look and feel for panels a painter attribute needs to be specified as follows.
Code Sample 2-39 — Specifying the synth logo painter for panels
The xui.synth file needs to be available on the classpath and in this case was placed in the resources folder. The xui.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.
Figure 2-5 — button.png and button_on.png used by synth to render buttons
• • • • 451 • • 2 – Carousel, User Guide
• • • 452 • • • 3 – Carousel, User Guide
Case Study 3 — Surveys
[This is a feature of Carousel] Carousel includes a specialized library for generation and management of survey applications. The surveys are intended for use on embedded devices such as handheld computers, webpads or PDAs but may also be used on desktop computers. Carousel also includes a facility for management of the survey data.
A typical survey
In this section we will show a sample survey application and highlight some of the common features that you may find useful in building a survey application. The example is taken from a real application for the hospitality industry but we have changed some of the text. The application was designed for use by guests at the reception desks of hotels.
• • 453 • • • • 3 – Carousel, User Guide
A welcome screen A welcome screen is often used to present the user with some information about the survey. In the example shown below we include the option to choose the language in which the survey is presented.
The survey is presented full screen so as to restrict access to the desktop. A special set of keystrokes is used to access an ‘easter egg’ that provides administration functions for the survey and allows the administrator to close the survey properly. Following the welcome screen a series of pages presenting questions to the user are shown. The exact content of these pages depends on the question set and possibly the history of the questionaire. The tools built into Carousel include the ability to rotate questions and insert conditional questions or so called ‘golden’ questions.
• • • 454 • • • Carousel, User Guide 3 –
Several techniques are available for formatting the questions and the Carousel survey library includes a number of custom widgets for generation of question widgets and custom widgets. The library also provides some extra widgets for things like progress and navigation control.
Finally the application presents a thank you screen that marks completion of the survey. Presentation of this screen also triggers saving of the survey data. The user can no longer go back and change the responses. The screen eventually times out and the welcome screen is presented again for the next survey respondant.
A survey wizard Surveys can be setup with a simple wizard that takes the user through a few steps to configure the survey content.
• • • • 455 • • Carousel, User Guide 3 –
First the user is requested to enter some general setup parameters:
Next a choice of styles and color schemes is presented:
• • • • 456 • • Carousel, User Guide 3 –
and then there are questions about the content of the survey and how it is to be presented to the user.
and finally some options about the survey generation and data source.
• • • • 457 • • 3 – Carousel, User Guide
If no data is available the survey wizard generates a set of tables in a database. Later the question manager can be used to setup the questionaire. Tables for the resulting data are also configured.
Managing the survey data
The survey libraries shipped with Carousel provide a simple mechanism for loading and saving surveys and survey data. Carousel even includes services for replication of questionaire database tables, fault tolerant transmission of data and tracking of responses.
• • • 458 • • • Carousel, User Guide 3 –
Setting up survey templates
Embedding a survey
Setting up a survey database
Configuing a survey
Using rules
Carousel includes a powerful rules based system for driving surveys. An example can be seen below:
Figure 3-1 — The Survey Editor
The rules system is designed for the simple sort of rules used in surveys and questionaires. However if you need a more complete rules engine one can be plugged in, Carousel’s survey and template system was designed with this capability in mind and some prototype implementations using third party rules engines are available on request.
• • • • 459 • • 3 – Carousel, User Guide
• • • 460 • • • 4 – Carousel, User Guide
Case Study 4 — Product selection
Building CoolSelector with XUI
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 Xoetrope's XUI, 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 Xoetrope 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 XUI
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.
• • 461 • • • • 4 – Carousel, User Guide
CoolSelector and its underlying data model provided much of the inspiration for XUI 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.
• • • 462 • • • Carousel, User Guide 4 –
The XUI experience
The XUI version of CoolSelector started as an experiment to see how XUI 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 XUI data model. XUI's data binding facilities immediately meant that a whole layer of data manipulation disappeared: instead of parsing responses and updating controls, XUI 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 XUI. XUI 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 XUI 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 XUI version, and the results were astounding. Again the XUI 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. XUI 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 XUI 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 XUI 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 XUI CoolSelector user will see little difference whether they are on- or off-line. From a corporate standpoint, XUI 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 XUI as an inherently open architecture could form the basis of an industry platform.
• • • • 463 • • Carousel, User Guide 4 –
Results
Direct comparison between the XUI 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 XUI version of the application represents a 75% reduction in code and approximately a 50% saving in the number of subsystems used. Selections with XUI 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 XUI 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.
• • • • 464 • • Carousel, User Guide 4 –
Remember CoolSelector is built using AWT and with just the components built into Carousel..
Figure 4-1 — CoolSelector’s settings page.
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.
Figure 4-2 — CoolSelector’s settings page in German.
• • • • 465 • • 4 – Carousel, User Guide
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.
Figure 4-3 — Specifying a load interactively and in context. 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.
Figure 4-4 — CoolSelector’s product recommendations support user modification.
• • • 466 • • • Carousel, User Guide 4 –
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.
Figure 4-5 — Detailed product information is available on or off-line.
The Web button provides a link to the Internet usings the JDIC web browser component.
Figure 4-6 — The embedded JDIC Web Browser.
• • • • 467 • • 4 – Carousel, User Guide
Ultimately the application is an e-commerce application and we get a list of part numbers ready for ordering.
Figure 4-7 — CoolSelector’s parts list ready for on-line ordering. For off-line users and for project mangement purposes CoolSelector also produces detailed summary information that can be saved for quality control purposes.
Figure 4-8 — A CoolSelector selection report.
• • • 468 • • • Carousel, User Guide 4 –
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.
Figure 4-9 — Reports can be exported to several popular office formats
• • • • 469 • • 4 – Carousel, User Guide
And for the techies in the business there are even diagrams showing the characteristics of the proposed systems operation:
Figure 4-10 — Detailed performance information.
• • • 470 • • • SECTION VI APPENDIX 4 – Carousel, User Guide
• • • 472 • • • A – Architecture Carousel, User Guide
Appendix A — Architecture
Carousel 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 Carousel and details how its subsystems interact. The appendix may be of use to application architects and to application programmers.
The XUI Solution
At its simplest XUI s a set of factories designed to hide some of the complexity involved in application construction. XUI 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 XUI library. The solution offered by XUI and enhanced by Carousel is layered in an object oriented fashion so there is considerable control as to how extensively XUI need be used in an application. At its fullest Carousel 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 Carousel consists of
• • 473 • • • • A – Architecture Carousel, User Guide
Page Component
XML Page Builders
Component Factory Validation Factory Binding Factory
Routing / Transports
Widget Library Event Handler Data Model
Services
Page Management Resource Management
Project
Applet/Application
Figure 1: Overall application structure Project support for coordination of resources Most applications deal with a variety of objects and resources and an XUI application is no different. In a bid to coordinate resource usage XUI 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 alos 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 XUI framework. An XUI 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. Factory methods to remove repetition Perhaps one of themost obvious places to begin our description of XUI 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. XUI provides several factory methods that take car of constructing the UI objects and setting the most common properties such as location and content. XUI also includes support for style
• • • 474 • • • Carousel, User Guide A – Architecture
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. Factories to target different devices/platforms One of the key design goals behind XUI was to target limited devices such as handheld computers. For this reason XUI 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 XUI 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. XUI also employs a pluggable architecture for registration of the component factories so there is litle difficulty extending XUI 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 XUI provides full access to the underlying API. Widgets and components created with XUI 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 XUI. If for some reason the XUI approach is found to be limiting then it can be supplemented or replaced by direct coding. This strategy also means that XUI applications can be extended by adding third party components and tools without restriction. Ultimately the XUI code base is open source and is available under a license that allows modification so (probably as a last resort) any of the XUI features could be modified to meet the applications needs. Layered approach to styles As has already been mentioned above XUI 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
• • • • 475 • • Carousel, User Guide A – Architecture
XStyleFactory class to construct the widgets instead of the basic component factory. In fact by default XUI 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 XUI 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 XUI employs an abstract registration mechanism whereby component factories can be added to an application. The XProject 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 XAttribuedComponent 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 XUI or Carousel frameworks but much advantage would be lost if such an approach is adopted. Applications rarely consist of a single screen or page so XUI 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 cylce. Depending on the type of application pages may be used on a one off basis or reused repeatedly, perhaps with updatable content. XUI 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 XPageManger class is owned by the project and in turn it owns the individual pages in the application. XUI 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.
• • • • 476 • • Carousel, User Guide A – Architecture
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 XPageDisplay 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 createion, activation, and deactivation.
Custom Page PageM anager Secondary PageLoader PageDisplay / Applet New page
showPage Since a page may be declared via XML it is not possible to directly loadPage loadPage instantiate an instance of a page class new Instance that will represent the content of the
read Page definition XML file. Instead the page manager delegates to a secondary loader Instantiate directly (registered at startup) that processes
update page history the XML file and creates and se tS ta tu s configures the page’s components. pageCreated During construction a instance of an
displayPage XPage is created or of a derived class and the components are addressed to
saveBoundValues the new page. Once create in this way the XML originated page is largely Add new page indistinguishable from one created rem ove old page from a Java class.
updateBoundValues 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. Figure 2 Page Display 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.
• • • • 477 • • A – Architecture Carousel, User Guide
Proxies for event management
Component Event Handler Page As with the UI construction XUI provides extensive support for event handling.
mouseClicked() Using XUI all the plumbing code needed to direct the application to an event response method is hidden from the programmer. Lookup response method There is no need to declare listeners or adapters, all the application need to is Invoke response method declare its interest in receiving notification of an event. To handle an event the XUI 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 XPage 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 XUI 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 XMinMaxValidation 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 XUI 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.
• • • 478 • • • Carousel, User Guide A – Architecture
Component Validation Handler Exception Handler 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 focusLost XValidationExceptionHandler class. By default Lookup validation it is the page that actually that implements and handles this response by displaying a warning or error Validate message. However the application can override the
handleException processing to provide a page specific way of handling the error or the application may choose to register a error level
error level 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 XUI 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 XModel 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 (XDataSource in the basic XUI setup). The data source can then load the data from various sources. Carousel’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.
• • • • 479 • • A – Architecture Carousel, User Guide
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 XUI 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 XDataBinding 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.
PageDisplay Object1 ListBinding List Adapter Data Model Node Bound ComboBox The data binding and the underlying model can be seen to act as a data
updateBoundValues channel for passing information from get one page to another without directly get
get referencing one page from another. Thus an axample of this might be where a page displays an input for a addItem user name or ID, if the input field for this information is bound to the
saveBoundValue model any other page that binds a UI set component to the same model node getSelectedItem will automatically display the same data once that page is displayed. set set 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 XUI package provides a limited set of adapters as itself provides only a limited set of model nodes, mostly for static data sources. Carousel on the other hand provides much richer
• • • 480 • • • Carousel, User Guide A – Architecture
and varied data sources. Indeed Carousel’s data model encapsulates remote data and remote services both of which may return unknown data types. Carousel 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. Carousel provides access to databases via the model. Tables can be preconfigured for specific queries (including parameterized queries) or the queries can be constructed dynamically. Carousel 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 Carousel 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. Carousel 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 Carousel introduces another special type of node to the model, an XServiceProxy 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
• • • • 481 • • A – Architecture Carousel, User Guide
effectively means calling that function. The function can be paramaterized by the nodes attributes and can be implemented in a variety of ways. In Carousel 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. Carousel 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 Carousel. 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. Carousel 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. Carousel’s support for routes does not stop with transport protocols. Carousel 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. Carousel’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).
• • • 482 • • • Carousel, User Guide A – Architecture
In addition to this direct loading of resources Carousel 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 XUI. 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.
• • • • 483 • • A – Architecture Carousel, User Guide
• • • 484 • • • B – Java language Carousel, User Guide
Appendix B — Java language
This chapter explains some of the key Java Language features that are used in Carousel. It is intended to highlight the impact of these features on Carousel 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: Carousel is a Java based system. The objects created by Carousel 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 Carousel framework. If something is missing or does not suit your purposes then you can just code it in Java. For example Carousel provides numerous ways of adding components but sometimes you may have special needs and these needs may not suit the Carousel 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 XUI and Carousel 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 Carousel application. For this reason it is recommended that you use the debug version of the XUI 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 XUI 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.
• • 485 • • • • B – Java language Carousel, User Guide
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 XUI 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 XUI. Again the debug versions of the libraries can be helpful and with Carousel 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 Carousel 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, XPage. The XPage 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 XUI 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 Carousel 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.
• • • 486 • • • Carousel, User Guide B – Java language
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
Carousel makes extensive use of classloaders both within the editor and at runtime. Carousel even includes a custom classloader so that you can access resources from the standard Carousel 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 Carousel as Carousel 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. Carousel 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
• • • • 487 • • Carousel, User Guide B – Java language
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 Carousel. 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 Carousel event handling (see “Event handling” on page 177) and dynamic attributes (see “Evaluated attributes and helpers” on page 273) are invoked via reflection.
Encodings
Encoding is sometimes an issue for Carousel and for Java applications. Carousel is affected by encodings when working with localized text (see “Localization” on page 231), 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 Carousel 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 Carousel 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.
• • • • 488 • • Carousel, User Guide B – Java language
Annotations
Carousel 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=xui_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 XButton 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 xui application lifecycle, but instead they can be created directly
@Page( "foobar.xml" ) public class MyClass { ... }
Table B-1 —
• • • • 489 • • B – Java language Carousel, User Guide
Annotation Usage
@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 XButton myBtn;
or
@Validate( value="CreditCard", method="validate", when="mouseClicked", extra="type=custom,message=msg_res_key" ) protected XButton myBtn;
with all values other than the value attribute being optional. The extra attribute is a comma separated list of name-value pairs.
Table B-1 —
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..
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
Table B-2 — Java learning resources
• • • 490 • • • Carousel, User Guide B – Java language
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.
Table B-2 — Java learning resources
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
• • • • 491 • • B – Java language Carousel, User Guide
• • • 492 • • • C – Startup properties Carousel, User Guide
Appendix C — 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 Carousel. 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.
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 NONE is 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.
Table C-1 — Startup properties
• • 493 • • • • C – Startup properties Carousel, User Guide
Property Role
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.
XDataSourceClass The name of the class that implements the datasource, by default this is net.xoetrope.optional.data.XOptionalDataSource
XLibVisualise 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 XLifeCycleListener 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 net.xoetrope.builder.XuiBuilder
DefaultModelClass Sets the default model class. Normally this is XBaseModel. The model class has a major role in Carousel 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 Carousel will save data to the System specified temporary directory.
Table C-1 — Startup properties
• • • 494 • • • Carousel, User Guide C – Startup properties
Property Role
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.
Table C-1 — Startup properties
And some properties introduced with XUI 2.0:
Property Role
LAFa 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 net.xoetrope.optional.log.XQueueLogWriter which 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.
Table C-2 — Startup parameters introduced in version 2.0 of XUI a.Swing only
And some properties introduced with XUI 3.0:
Property Role
BuilderClass[N] TThe 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.
Table C-3 — Startup parameters introduced in version 2.0 of XUI
• • • • 495 • • C – Startup properties Carousel, User Guide
• • • 496 • • • D – Embedded support Carousel, User Guide
Appendix D — Embedded support
Carousel 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 XUI 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 XUI libraries. Removing the shutdown hooks The desktop build of XApplet 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
• • 497 • • • • D – Embedded support Carousel, User Guide
may be necessary to exclude the classes in the net.xoetrope.xui.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 Xoetrope directly.
• • • 498 • • • E – Reference Carousel, User Guide
Appendix E — 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:
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.
Table E-1 — Common component attributes
• • 499 • • • • E – Reference Carousel, User Guide
Attribute Usage
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
Table E-1 — Common component attributes
A number of components use the XAttributedComponent interface to set special attributes, these are listed below:
Component Attribute Usage
XCheckbox selected Sets the selected state of the check box, values=true|false
XImage 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.
XLabel 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
Table E-2 — Component specific attributes
• • • 500 • • • Carousel, User Guide E – Reference
Component Attribute Usage
XPanel 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.
XRadioButton 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
XTable headingStyle Sets the header style, the style refers to the styles listed in the XUI 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
XButton alignment Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default), SwingConstants.LEFT, SwingConstants.CENTER, SwingConstants.LEADING, SwingConstants.TRAILING
XEdit 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
Table E-2 — Component specific attributes
• • • • 501 • • Carousel, User Guide E – Reference
Component Attribute Usage
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.
XPassword alignment* Sets the horizontal alignment of the icon and text: SwingConstants.RIGHT (the default), SwingConstants.LEFT, SwingConstants.CENTER, SwingConstants.LEADING, SwingConstants.TRAILING
XScrollableMet horizontal Determines when the horizontal scrollbar appears in the aContent scrollbar* scrollpane. Legal values are:"as needed", “always” and “never”
vertical Determines when the vertical scrollbar appears in the scrollbar* scrollpane. Legal values are:"as needed", “always” and “never”
XScrollbar horizontal Determines when the horizontal scrollbar appears in the scrollbar* scrollpane. Legal values are:"as needed", “always” and “never”
vertical Determines when the vertical scrollbar appears in the scrollbar* scrollpane. Legal values are:"as needed", “always” and “never”
XSplitPane 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.
XTable2 headingStyle Sets the header style, the style refers to the styles listed in the XUI style file.
Table E-2 — Component specific attributes
• • • • 502 • • Carousel, User Guide E – Reference
Component Attribute Usage
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
XTextArea 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
XPage 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
Table E-2 — Component specific attributes
*applies to the Swing version of the component only
• • • • 503 • • E – Reference Carousel, User Guide
• • • 504 • • • F – Upgrading Carousel, User Guide
Appendix F — Upgrading
A significant number of changes have occured between version 1.0.4 of XUI and XUI 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 XUI library.
Projects
In earlier versions of XUI 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. XUI 2.0 does away with almost all of these statics and instead has one central reference point, the XProjectManager. 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 XProjectManager and XProject 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 XPage class now contains a protected member project.
Projects
As part of the restructuring of projects the XResourceManager 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 XResourceManager 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.
• • 505 • • • • F – Upgrading Carousel, User Guide
XPage split
Another major change introduced in XUI 2.0 is the splitting of the XPage class. The XPage 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 XPageHelper 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 XUI 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 XUI 2.0. Examples of this include the NavigationHelper class that now employs library functions whereas previously in necessitated a class derived from XPage. XUI 3.0 deprecates the NavigationHelper class as it no longer servers much use.
• • • 506 • • • Carousel, User Guide F – Upgrading
Deprecation
Several classes are now deprecated, these classes are moved to the net.xoetrope.deprecated package. The classes remain functional but be advised that we will probably drop the classes from the next major distribution of XUI.
Omitted components
XUI 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, KALIDEOSCOPE is now a powerful tool with which to build XUI and Carousel 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.
• • • • 507 • • F – Upgrading Carousel, User Guide
• • • 508 • • • G – Building XUI Carousel, User Guide
Appendix G — Building XUI
The complete source for both XUI and KalIDEoscope 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.
Parameter Value
Server https://svn.sourceforge.net
Path /svnroot/xui/
Table G-1 — Subversion connection parameters
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:
Project Role
XUI The main XUI project
XuiALL Builds the XUI NBM, NetBeans module
XuiSuite Ties the various NBMs together.
KalIDEoscope The KalIDEoscope editor NBM
lib A collection of third party libraries
javac JDK files
Table G-2 — Projects in the SVN repository
The javac project is not completely contained in the repository due to licensing issues.
• • 509 • • • • G – Building XUI Carousel, User Guide
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 XUI 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 XUI, XuiSuite and KalIDEoscope 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: Building Building the project should now be straightforward. 1. First build the XUI project by right clicking in the project view and choose Clean and Build All. 2. Now open the XUI module under the XuiSuite project. When the project opens (it should have a different icon to the main XUI project) build it, this creates the .nbm file for XUI project. This file is referenced by the KalIDEoscope module. 3. Build the KalIDEoscope 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. • • • 510 • • • H – Kalideoscope for Eclipse Carousel, User Guide Appendix H — Kalideoscope for Eclipse KalIDEoscope 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 “Creating a new project” on page 49. Figure H-1 — Select the XUI Project wizard. • • 511 • • • • H – Kalideoscope for Eclipse Carousel, User Guide Figure H-2 — Name the project. Figure H-3 — Choose the main project settings. • • • 512 • • • Carousel, User Guide H – Kalideoscope for Eclipse Figure H-4 — Setup the frames if necessary. Figure H-5 — Name/Choose the configuration files. • • • • 513 • • H – Kalideoscope for Eclipse Carousel, User Guide Figure H-6 — Customize and finish the project setup. Accessing SVN The Subversion repository can be access anonymously either via the command line or via a tool such as SmartSVN or TortoiseSVN. Parameter Value Server https://svn.sourceforge.net Path /svnroot/xui/ Table H-1 — Subversion connection parameters 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: Project Role XUI The main XUI project XuiALL Builds the XUI NBM, NetBeans module XuiSuite Ties the various NBMs together. KalIDEoscope The KalIDEoscope editor NBM Table H-2 — Projects in the SVN repository • • • 514 • • • Carousel, User Guide H – Kalideoscope for Eclipse Project Role lib A collection of third party libraries javac JDK files Table H-2 — Projects in the SVN repository 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 XUI 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 XUI, XuiSuite and KalIDEoscope 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: Building Building the project should now be straightforward. 1. First build the XUI project by right clicking in the project view and choose Clean and Build All. 2. Now open the XUI module under the XuiSuite project. When the project opens (it should have a different icon to the main XUI project) build it, this creates the .nbm file for XUI project. This file is referenced by the KalIDEoscope module. 3. Build the KalIDEoscope 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. • • • • 515 • • H – Kalideoscope for Eclipse Carousel, User Guide • • • 516 • • • I – Widgets Sets Carousel, User Guide Appendix I — Widgets Sets Carousel and XUI support a number of widget sets, some sharing a common ancestory and others significantly different. Carousel 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 XUI. Most of the advanced graphcal components supported by Carousel 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 XUI 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 Carousel’s support. The AWT and Swing widget support in XUI 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 XUI. The applet/application has also been updated to share the very latest XUI 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 XUI International Soccer Manager, demonstrates how to build a complete application using XUI and SWT. HTML While XUI 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 • • 517 • • • • I – Widgets Sets Carousel, User Guide 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. XUI’s J# support is functional, but should still be regarded as experimental. Others Some third parties have used XUI with other widgets sets on mobile devices (e.g. the LCDUI), please see the XUI 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 XUI’s widget abstraction is no different. That said XUI 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 • • • 518 • • • J – What’s new in version 3.0 Carousel, User Guide Appendix J — What’s new in version 3.0 The Juice project (the codename for XUI and Carousel 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 XUI features The features presented below are listed in the order in which they were added to XUI. Added support for multiple page loaders Previously only a single page loader could be used and normally this was the XuiBuilder 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 XPageManager.setSecondaryLoader method to XPageManager.addSecondaryLoader and adding a return value to the XPageLoader.loadFrames method Modified the build and the XContentHolder interface The build and the source for the full set of JDKs/compiles has been modified. Previously the XContentHolder 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, • • 519 • • • • J – What’s new in version 3.0 Carousel, User Guide zooming, docking etc... and should be considerably more flexible and reliable. The docking framework does not rely on other parts of XUI and can be used independantly. Figure J-1 — Export to Excel or OpenOffice 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 XuiBuilder is the initial builder it will get preference and will make teh 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 XToolbarButton 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 XSplashWindow class, based on work by Werner Randelshofer. The new class is set as the main entry point, loads and then invokes the XUI 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 • • • 520 • • • Carousel, User Guide J – What’s new in version 3.0 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: Table J-1 — Reflective component property registration 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 XBaseValidator.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 Table J-2 — Specifying a system colour 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 XStyleEx instead of the basic XStyle 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. • • • • 521 • • J – What’s new in version 3.0 Carousel, User Guide 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 XAttributedComponent 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 1. 11. Table J-3 — Extended styles 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 XUI. The applet/application has also been updated to share the very latest XUI 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 XUI International Soccer Manager, demonstrates how to build a complete application using XUI 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. • • • 522 • • • Carousel, User Guide J – What’s new in version 3.0 Annotation support added Added annotation support such that the page class can have the following annotations @Page( "foobar" ) class MyClass extends XPage { @Find private XButton myBtn; @Bind( "a/b/c" ) private XEdit myInput; @Validate( "CreditCardRule" ) private XEdit cardNumber; @Event( method="doProcessing", type="ActionHandler" ) private XButton processBtn; ... public void doProcessing() { ... } ... } Table J-4 — Annotations 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. • • • • 523 • • J – What’s new in version 3.0 Carousel, User Guide 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: 1. Table J-5 — A customized component the customizer attribute refers to a customization specified in the customizations.xmlfield. The customization file is as follows: Table J-6 — Specifying customizations • • • 524 • • • Carousel, User Guide J – What’s new in version 3.0 The results of this customization is a table like the following, with row striping and customized header: Figure J-2 — A customized table XPainter replaced with SwingX Painter The painter classes have been refactored to extend the SwingX Painter class. The SwingX interface was very close to the XUI 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 XUI 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 XUI at the root of the POJO model. POJOs or Plain Old Java Objects support has been added in XUI 3.0. The new support for using POJOs as a backing for the XUI 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 XUI at the root of the POJO model. Here's a sample configuration, showing how some POJO properties are overridden and some finders are refined. Table J-7 — Pojo mappings • • • • 525 • • J – What’s new in version 3.0 Carousel, User Guide The POJOs are then attched via the root object that serves to connect the XUI model and POJO hierarchy. A sample usage is as follows: 1. Table J-8 — Pojo binding 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 XUI 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 XDataBindingFactory 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: StartupObject Table J-9 — Listing the startup objects where • • • 526 • • • Carousel, User Guide J – What’s new in version 3.0 parameter types are set using the JVM type signature syntax, for example Table J-10 — Reflective property 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. // In startup.properties specifc the Lifecycle object LifeCycleListener=com.xoetrope.beaumont.ProjectListener // In that object map the "START_PAGE" page name public class ProjectListener implements XLifeCycleListener { /** * Called when the application/applet has been created and initialized. * @param project the owner project */ public void initialize( XProject p ) { String[] args = (String[])project.getObject( "StartupArgs" ); // Set the start page to the 3rd command-line parameter p.getPageManager().mapPageName( "START_PAGE", args[ 3 ] ); } } // In the frames.xml include the "START_PAGE" key Table J-11 — Mapped page names Minimal http server embedded A minimal http server is now embedded at net.xoetrope.optional.http.XHttpServer. 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. • • • • 527 • • J – What’s new in version 3.0 Carousel, User Guide 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. Figure J-3 — The system tray under Windows XP 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 XUI application. To use this support the application must add an instance of the net.xoetrope.swing.deploy.XSystemTrayManager class. A good place to do this is in a XLifeCycleListener implementation public class ProjectListener implements XLifeCycleListener { private static XSystemTrayManager sysTray; /** * Called when the application/applet has been created and initialized. * @param project the owner project */ public void initialize( XProject proj ) { final XProject project = proj; if ( sysTray == null ) { sysTray = XSystemTrayManager.getInstance( project ); // Do remaining initialization and network access } } /** * Called when the application/applet has been shutdown and is about to exit */ public void shutdown() { } } Table J-12 — Using the system tray XuiProxy added to ease Xui-Swing integration The proxy can be used in place of an applet class where the XUI fraction of an application needs to be integrated with an existing or legacy Swing application. The proxy extends JPanel and encapsulates the XUI elements of the project. An example application is included in the XUI 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 • • • 528 • • • Carousel, User Guide J – What’s new in version 3.0 class XCollectionTableModel supports the new XTableModel, XRowModel and XFieldModel setup. For example. public class MyTable extends XPage { ... public MyTable() { String[] names = { "ID", "Title", "Author" }; String[][] rawData = { { "0", "Moby Dick", "Herman Melville" }, { "1", "The Hunchback of Notre Dame", "Victor Hugo" }, { "2", "The Idiot", "Fyodor Dostoevsky" }, { "3", "Ulysses", "James Joyce" }, { "4", "Thus Spake Zarathustra", "Friedrich Nietzsche" }, { "5", "Bleak House", "Charles Dickens" }, { "6", "Mansfield Park", "Jane Austen" }, { "7", "Alice's Adventures in Wonderland", "Lewis Carroll" }, { "8", "The Republic", "Plato" }, { "9", "Kidnapped", "Robert Louis Stevenson" }, { "10", "On the Duty of Civil Disobedience", "Henry David Thoreau" }, { "11", "The Jungle Book", "Rudyard Kipling" }, { "12", "The Picture of Dorian Gray", "Oscar Wilde" }, { "13", "The Rime of the Ancient Mariner", "Samuel Taylor Coleridge" }, { "14", "Catcher in the Rye", "J. D. Salinger" }, { "15", "The Glass Bead Game", "Herman Hesse" }, }; ArrayList fieldNames = new ArrayList(); for ( String n : names ) fieldNames.add( n ); ArrayList data = new ArrayList(); for ( String[] row : rawData ) { ArrayList rowList = new ArrayList(); for ( String field : row ) { rowList.add( field ); } data.add( rowList ); } XCollectionTableModel ctm = new XCollectionTableModel( project, "CollectionTable", fieldNames, data ); rootModel.append( ctm ); } ... } Table J-13 — Using Java arrays with table bindings • • • • 529 • • J – What’s new in version 3.0 Carousel, User Guide combines with the XML to bind the collection to the 'collectTable' table component.: ... Table J-14 — Binding to a Java array New Kalideoscope 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 • • • 530 • • • Carousel, User Guide J – What’s new in version 3.0 mode, and a third mode with showing selected property values. Clicking on these hints gives access to a single property editor for that property. Figure J-4 — Toggling in-situ editing Support for evaluated attributes added Support for evaluated attributes within the Kalideoscope 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. Figure J-5 — Expression editing • • • • 531 • • J – What’s new in version 3.0 Carousel, User Guide 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. Figure J-6 — Dialog size preview 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. Figure J-7 — Set the preferred dialog size • • • 532 • • • Carousel, User Guide J – What’s new in version 3.0 New Carousel 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. XGenericBuilder A generic builder has been added. The builder allows a variety of file formats to be mapped to XUI 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 XUI components, or multiple elements within the source file can operate on a single component. XPojoFormBuilder Extending the XGenericBuilder, 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 XPojoModel objects visualization has been added to the Data Visualizer. The generic builder also makes use of XUI'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 Carousel 3.0 adds many new components, some of which are listed below. Many other components are also enhanced and improved. Component Description XCalendar 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 XRollup An outlook like rollup bar. Each category within the rollup bar is a panel, allowing you complete control of the bar's content. XTimeChooser A drop down list for choosing the time of day, a length of time or a start/end time. Table J-15 — New Components • • • • 533 • • J – What’s new in version 3.0 Carousel, User Guide Component Description XBreadcrumbBar Displays a list of links to previous pages in the page navigation history. XWizard A dialog class that can display and manage the pages of a Wizard. XCheckCombo A combo box that shows a list of options that can be toggled using combo boxes Table J-15 — New Components Other new components The Carousel suite of products goes beyond the immediate developer tools and covers line of business applications such as CRM, Product Selection, Marketing and Office automation. Not all of these products are open source and not all are on the same relase cycle as the developer products and therefore they are not covered in this manual. More information is available on the Xoetrope website, but at the time of writing some of the additional components include: Carousel CRM Carousel Maps Carousel Surveys Carousel Docs Carousel Mail Upgrading your project • • • 534 • • • 1 – INDEX Carousel, User Guide INDEX Symbols build.xml 53 BuilderClass 192, 350, 494, 495 .NET 35 Building pages 89, 103 Business logic 179 A About 19 C accumulateMessages 207 CachedDatabaseTable 263 Action 220 Callbacks 167 ActionHandler 181 CD 362 Adapters 168 Center on screen 51 addActionHandler 186 CenterWin 494 addComponent 118 Centre 84 addConfigFile 175 Character encoding 235 addConnection 264 checkValidations 113, 204, 207 addHandler 186 Choosing the Language 231 addListener 186 Class names 56 addSecondaryLoader 519 classpath 500 addValidation 121 ClientHeight 493 Advanced mode 311 ClientWidth 493 AJAX 33 codec 409 Ajax 36 ColumnLayout 154 alignment 500 Columns 145 Animations 393 Command-line 63 Annotations 123, 489 Compilation 62, 125 ANT 125 Component customization 524 Anti-aliasing 495 Component Factory 494 Apache 34 Component factory 117 Applet 64 Component inspector 59 Application Styles 157 ComponentAdapter 313 Application styles 195 ComponentFactory 110 Application title 84 ComponentRegistry 494 Architecture 33 Components 58, 381 ArrayLists 265 Configuration 163 ASP 64 Connectivity 37 AWT 60, 63, 190, 309, 517 Constraint 111 Content 107 B Content area 191 Context 75 Bar chart 405 Context sensitive 220 Bind 123 Controller 25 Binding 25 Conventions 19 Blackberry 35 CreMe 497 Branding 37 Current page 191 BuddyHelper 123 • • 535 • • • • 1 – INDEX Carousel, User Guide D Frames 51, 55, 85, 108, 154, 155, 191 Frameset 90 Data binding 75, 114, 120, 160 frameset 58 Data components 399 Data model 74 Datasets 75 G datasets.xml 281, 494 DatbaseTransferManager 265 getCurrentEvent 114, 182 Debugging 66 getCurrentPage 191 DebugLogger 124 getCurrentUser 168 DebugZones.properties 368 getModelText 255 DefaultEncoding 235 getValidation 210, 211 DefaultModelClass 494 Goodies 220 Deployment 357 Graphical components 381 Desktop 51 Groovy 174, 275 Dialog size preview 532 GuideLayout 143, 387 DISTINCT 261 DoubleAdapter 124, 486 H Drag and drop 326, 345, 519, 526 Dynamic Bindings 168 h 107 Hand-coding 377 handleException 206, 208 E Headers 108 Height 84 Easter egg 454 HelloWorld 117 Eclipse 42, 47, 66, 511 Helpers 124 Embedded applications 198 Hints 149 Embedded devices 497 History 191 EncodedLanguageResourceBundleLoad- Home page 156 er 235, 521 horizontal 500 Encoding 235 HSQLDB 99, 264, 523 Esmertec’s 497 HTML 33, 64, 85, 191, 276, 343, 517 Event 123 Html 388 Event handling 113, 177 HtmlBuilder 350 Excel 333, 342 HtmlFormLayout 154 Exception handling 184, 206, 276 Exporting 248 ExpressionEvaluator 174, 275 I Extended Styles 134 IBM 34 IDE 118 F Imported screens 109 Include 101 File name configuration 52 include 110, 152 Files view 53 Infopath 36 Find 123 Initial page 85 findRows 262 Input controls 395 Focus 181 Input validation 73, 203 FocusHandler 181 In-situ editing 95, 530 FOSS 21 • • 536 • • • • 1 – INDEX Carousel, User Guide Inspector 57 loadFrames 519 Installer 363 Logging 124 IntegerAdapter 124, 486 LogLevel 51, 85, 494 Internet Explorer 34, 497 LogWriter 125 Interpreter 174, 275 Look and feel 137 ItemHandler 181 izPack 363 M Mandatory validations 113 J MDI 157 J# 518 MenuHandler 181 J2EE 35 MessageHelper 124 Java 34, 67, 117, 485 Microsoft 34 Java beans 310 Microsoft Excel 248 Java console 205 Mime-type 64 Java Media Framework 409 minmax 205 Java Web Start 64, 359 modal 225 Java web start 43, 62 Model 25 JDBC 43, 257 ModelData 162, 494 JDIC 320 Model-View-Controller 24, 159 JDK 65, 497 Modularity 37 Jeode 497 MouseHandler 181 JGoodies 138 MouseMotionHandler 181 JNLP 64, 359 Multimedia 409 JPEG 335 MVC 159 JSP 64 JVM 201, 518 JWS 64, 200 N Name 107 NamedConnectionManager 264 K Naming convention 56 KeyHandler 181 Navigation 108 kiosk 397 NavigationHelper 124, 506 Nested panels 59 net.xoetrope.optional 257 L NetBeans 25, 43, 62, 66, 269 LAF 138 NSI’s 497 Language Editor 533 NumberFormatter 124 LayerLayout 154, 523 NumberValidation 210 Layout manager 108, 110, 143 NumBuilderClasses 495 LCDUI 35 NumComponentFactories 494 Learning Java 490 Legacy application 36 Licensing 26 O LifeCycleListener 494 Open source 21 Line chart 404 OpenOffice 333, 343 Linking components 170 Oracle 34 • • 537 • • • • 1 – INDEX Carousel, User Guide Output node 167 R Real-time 498 P Reflection 312 Registry editor 309 Package name 51, 85 Repeat 109, 309 Packaging 357 resources 191, 500 Packing 224 RIA 27 padding 224 Rich client 23 Page designer 57 Routes 279 Page display 166 Rules 73 Page history 189 run.bat 359 Page lifecycle 78, 190 Running applications 63 Page loaders 192, 519 Runtime 58 Page manager 78, 190 Page size 55 Page transition 113 S pageActivated 65, 79, 123, 166, 190, 226 pageCreated 65, 78, 190, 226, 485 Samples 46 pageDeactivated 79, 166, 190, 226 saveBoundComponentValues 166 PageManager 522 SavePath 494 Painters 141 ScaleLayout 143 Panels 108 Scanning a Project 234 PDF Exporter 533 Scatter plot 403 Performance 37 Screen size 84 PersonalJava 497 Scrollpane 108 Pie chart 407 SDI 157 plumbing 25 Separation of roles 37 PNG 138 Server side 34 point-of-sale 397 Servlet 34 POJO 525 setCaption 226 Popup Window 51 setDistinct 261 Popup window 85 setEventHandler 185, 186 Portable coding 518 setExceptionHandler 121 PreparedStatement 262 setModal 225 PreparedStatements 300 setResizable 226 Printing 333, 349, 353 setSaveOnClose 228 Project name 84 setSqlStatement 169, 262 Project view 53 setUseNativeHeaders 226 ProjectManager 42 setValidationColors 215, 521 ProjectPath 495 showDialog 224 Projects 49 showMessage 209 Properties palette 59 showPage 108, 189, 190 showPrevious 189, 191 shutdown 497 Q Shutdown hook 65 Sidebars 108 Question Manager 533 SkinBuilder 138 Smart-client 23 • • 538 • • • • Carousel, User Guide 1 – INDEX Soap 35 Source code 60 Source Nodes 167 Splash Screen 51 Splash screen 322 SQL 172, 262, 301 Stacked bar chart 406 Standalone mode 264 StartClass 493 StartPackage 493 Startup page 51, 85 Startup properties 522 startup properties 350 Startup sequence 63 startup.properties 162, 191, 192, 194, 203, 312, 358, 493 Static data 161 Struts 34 Style 107 style 501, 502 Style palette 97 StyleFile 494 Styles 60, 71 styles.xml 494 Stylesheets 118 Sun 34 Support 20 Survey wizard 455 SurveyManager 42 Surveys 453 SVG 138, 329, 335 Swing 35, 43, 60, 63, 190, 309, 363, 517 SwingComponentFactory 314 SwingLabs 141, 519 Switching toolkits 125 SWT 517 Synth 87, 138 System colors 133 SystemColor 521 T Target Area 157 Target areas 191 Text 386 TextHandler 181 Title 493 Toolbars 108, 221 • • • • 539 • • 1 – INDEX Carousel, User Guide Toolkits 125 Transfer 265 Translation 231 TreeTable 408 TriggerValidations 495, 522 Troubleshooting 375 U updateBindings 166, 168 updateBoundComponentValues 166 URL 191 UseWindow 493 V Validate 123 validate 211 validate.xml 494 Validation 73, 203 ValidationFactory 494 Validations 112, 121, 494 validations.xml 112, 203, 212 View 25 Visual feedback 191 Visualization 99, 533 W w 107 W3C 329, 350 wasMouseClicked 119 Welcome 454 WHERE 262 Widgets 85 Widgets sets 517 Width 84 Window Size 51 Windows CE 497 Workflow 343 X x 107 XActivation 200 XAML 35 XAnimatedText 393 • • • 540 • • • Carousel, User Guide 1 – INDEX XApplet 63, 358 XAttributedComponent 313, 522 XAudio 409 XBaseModel 494 XBaseTable 395 XBaseValidator 210, 212 XBreadcrumbBar 534 XButton 105, 118 XCalendar 533 XCaptionedImage 385 XCheckbox 105 XCheckCombo 534 XCheckList 396 XClickListener 384 XCollectionTableModel 266 XComboBox 105 XComponentConstructor 314 XContentHolder 198, 519 XCreditsText 394 XDataBindingFactory 526 XDataSource 252 XDataSourceClass 494 XDateEdit 397 XDialog 105, 223, 323 XEasterEgg 384 XEdit 105, 118 XEventAdapter 183 XFieldModel 266 XFlowedTextComponent 386 XForms 35 XGraph 399 XHotspotImage 105 XHtmlText 388 XImage 105 XImageMap 105 XKeypad 397 XLabel 105, 118 XLayoutHelper 112 XLibVisualise 494 XLifeCycleListener 200, 201, 528 XList 106 XMarqueeText 395 XMenu 106 XMenuBar 106 XMenuItem 106 XMessageBox 106 XMetaContent 106 XML 33, 103 • • • • 541 • • 1 – INDEX Carousel, User Guide XModelNodeHelper 124 XMoneyEdit 398 XOptionalDataSource 258, 494 XPage 77, 104, 113, 117, 212 XPageLoader 192 XPanel 106 XPassword 106 XPath 159 XPojoFormBuilder 533 XPojoModel 533 XPolygonalTextArea 389 XProject 65 XProjectManager 88 XRadioButton 106 XReflectedImage 384 XRegisteredComponentFactory 316 XRegisteredDataBindingFactory 175 XRollup 533 XRotatedText 392 XRowModel 266 XScrollableMetaContent 106 XScrollPane 106 XShape 381 XSpinner 398 XSplashWindow 324, 325 XStyleEx 521 XSystemTrayManager 200, 528 XTab 106 XTable 106 XTableModel 266 XTableModelHelper 124 XTabPanel 106 XTextArea 106 XTextBinding 165 XTimeChooser 533 XTreeTable 407 XUI 21, 381 xui_state 172 XuiBuilder 519 XUL 35 XValidationExceptionHandler 206 XValidationFactory 209 XValidator 204 XVideo 409 XWizard 534 • • • 542 • • • Carousel, User Guide 1 – INDEX Y y 107 Z z-order 71 • • • • 543 • • 1 – INDEX Carousel, User Guide • • • 544 • • •