Guise™ Overview

Guise™ is a graphical user interface framework for Java designed from the ground up to be both simple and elegant. If JavaServer Faces were more like Java Swing, yet simpler and with generics, it would give you an idea of what you will find in Guise.

Framework

Guise sports an elegant, extensible, and easy-to-use set of building blocks. The main parts of the Guise framework are the following, from the broadest to the most fine-grained:

Guise
The Guise class represents the Guise installation. Only one instance of Guise exists for any JVM instance. The Guise class is useful for getting broad information about the state of the entire Guise framework, but it otherwise provides little functionality.
GuiseContainer
The GuiseContainer is set up automatically by the particular platform of Guise (e.g. web or Swing) you are using, and manages the installed Guise applications. Most of the time it's sufficient to allow the GuiseContainer to do its work on its own.
GuiseApplication
Each GuiseContainer can have multiple GuiseApplications installed. If you need added functionality, you can create your own GuiseApplication, but it's likely the platform you use will set up a default GuiseApplication if you take no special action. The web Guise platform, for instance, will automatically create a default GuiseApplication and bind your specified frames to the navigation paths you specify. For most purposes, this default GuiseApplication is all you need.
GuiseSession
Depending on the platform, there may be several users accessing the GuiseApplication—or even a single user accessing the GuiseApplication using various methods. Each of these access methods are allocated a GuiseSession. Each GuiseSession represents a user interaction with the GuiseApplication, and has its own state such as navigation, control values, even including perhaps a different language. The most common way to retrieve a Guise session is using Component.getSession()
GuiseContext
A GuiseContext is a short-lived object that helps a controller work with its associated component and view. On the web platform, for instance, the TextGuiseContext provides the controller with a writer for constructing XHTML documents. A GuiseContext manages the model-view-controller controller states.

Most of the time, the default implementations of the Guise pieces work together automatically, and you can concentrate on arranging the components of your user interface.

Component Model

A Guise user interface is made up of Components. A Guise Component follows the model-view-controller (MVC) paradim by containing one or more Models, while a Depictor for the appropriate platform updates the view on that platform. Not all Guise components contain models, however, although some component contain several.

For the Guise components that contain models, the components delegate data access to an internal, hidden reference to the model. The components in turn implement the model interfaces. This has two implications. First, the component model's information can be accessed directly through the component as if it were the component's properties. Second, an existing component can be passed to another component for data sharing. For instance, one ValueControl, which implements and contains an instance of ValueModel, can be passed to another ValueControl. These components will be linked by a single value model and be updated accordingly.

Several models, such as LabelModel, are found throughout the Guise framework from GroupPanel to CheckControl, either on their own or as base classes to other model types. A component's model cannot be changed once the component is created, but its model's values can be changed. If no model is specified, components will usually create a default model delegate.

All Guise components that are CompositeComponents can have child components, although they may not allow them to be changed. A special composite component called a Container allows custom adding and removal of child components, along with a Layout that defines how the child components should be arranged in the view. Because Guise containers implement Iterable, applications may use the short iteration form for(Component childComponent:compositeComponent) to iterate over container child components.

Almost every component and model allows other objects to listen for changes through property change listeners. In response to a property change event, a listener may update the other components whenever it chooses. There is no need to worry about view or context state—the Guise framework handles all that automatically.

The following are commonly-used components in the Guise framework:

Frame
Represents the top-most component in a hierarchy. Each application is assigned an ApplicationFrame, inside which a Component is bound to a navigation path and represents a point of navigation. Other frames may be instantiated separately. A Frame can contain a single child component, its content component.
Panel
A general container within a frame that allows custom collections of child components with a specified layout.
ModalNavigationPanel
A special navigation panel that represents a point of modal navigation (i.e. the user cannot exit the panel until modality ends). A ModalNavigationPanel does not fully get modal status unless another component requests modal navigation, as explained under "Navigation".
LayoutPanel
A lightweight panel for laying out several components together.
GroupPanel
A panel for semantically grouping together several components with an optional label.
Label
A string of text for labeling some object.
Heading
A string of text representing a heading at a certain nested level.
TextControl
Allows users input of information in text format. Although the data entered into a TextControl is in the form of text, this does not mean that the value a TextControl represents has to be a string. A TextControl is a generics-aware class that can represent several value types, including String, Integer, Float, and even Boolean. The control can have single or multiple rows.
CheckControl
Represents a Boolean value and allows the user to change the value with a check mark. A CheckControl can be represented either as a rectangle (a traditional checkbox) or as an ellipse (a traditional radio button). Whether the value of a particular CheckControl influences the values of other CheckControls depends on whether the CheckControls belong to the same ValuePolicyModelGroup. Radio buttons can be created, for example, by adding several CheckControls with ellipse check types to a MutualExclusionPolicyModelGroup.
SliderControl
A control that contains a number-based ValueModel, representing the contained value as a slider. For a slider control to function properly, its value model should be assigned a RangeValidator. A slider control can share its model with other controls, such as a text control.
Button
An ActionControl that is represented as a traditional button.
Link
An ActionControl that is represented as a traditional link.
ListControl
A control containing a ListSelectModel represented as a list of items for selection. The ListSelectModel implements the List collection class and is generics-aware.
Text
A component that produces text to view. Although the value can be set manually, this component is most useful when configured to dynamically load large sections of locale-aware text from configured resources.
Table
A powerful yet simple-to-use component that presents optionally editable data in a tabular format.
TreeControl
A component that presents optionally editable data in a collapsible tree structure.
Menu
A group of components and sub-components that can be presented as a vertical or horizontal pull-down menu. Menus are completely internationalized, and automatically rearrange based upon locale and component orientation.

Listeners

Most Guise application logic occurs by installing a listener into a component; delegate model changes are passed to registered component listeners. Listeners are installed using the standard Java paradigm of addXXXListeners(). Once a component changes, the listener may respond instantly by changing a model's values, by requesting navigation, or by performing some other action.

Validation

A Validator can be installed in a ValueModel using ValueModel.setValidator(Validator), after which the value model will automatically validate every attempt to update the value model's value. (The exceptions are ValueModel.clearValue(), which sets the value to null without validation, and ValueModel.resetValue(), which reverts the value back to the default without validation.) Because ValueControl is a value model, all value controls, such as TextControl, support the installation of a validator. One convenient validator for strings, for example, is RegularExpressionStringValidator.

Each component, including composite components, keeps a real-time validity status which can be accessed using Component.isValid(). A composite component is valid whenever all its child components are valid. An application can listen for a change in a component's Component.VALID_PROPERTY to be notified in real-time when an individual component or a composite component's validity status is changing.

To perform custom validity checks that cannot be handled by a validator (to determine if two password fields are identical, for example), one should override Component.determineValid() (and not Component.isValid()), taking care to call the parent version of the method before performing custom validity checks.

Navigation

All navigation within a Guise application is done using relative navigation paths. Navigation paths do not not begin with the slash ('/') character, and are interpreted relative to the Guise application base path. The Guise application description document is used to map navigation paths to navigation panels. On the Guise web platform, the web.xml file is used to indicate the Guise application description document. For example, imagine the following configuration:

In this configuration, the Guise container base path would be /server/webapp/, while the Guise application's base path would be /server/webapp/example/. The following web.xml file would reference the description document for your Guise application, application.turf:

<servlet>
<description>Guise Example Application</description>
<servlet-name>guiseExampleApplication</servlet-name>
<servlet-class>com.guiseframework.platform.web.GuiseHTTPServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>application.turf</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

The referenced Guise application description document would be stored at /server/webapp/WEB-INF/application.turf and would be described using URF PLOOP. It would map /server/webapp/example/ and /server/webapp/example/about to the navigation panel classes com.example.HomePanel and com.example.AboutPanel, respectively, using navigation paths:

`URF:
"guise"~<java:/com/guiseframework/>
"example"~<java:/com/example/>

*guise.DefaultGuiseApplication:
destinations=[
*guise.ComponentDestination("", example.HomePanel)
*guise.ComponentDestination("about", example.AboutPanel)
]
;
.

A Guise session may at any time request that navigation be transferred to another location by calling GuiseSession.navigate(String) or GuiseSession.navigate(URI). This navigation request can be overridden by later calls to the method, or some later event may override navigation.

If a relative path is specified, navigation to another navigation panel within the application will occur; if an absolute path is specified, navigation will occur to another location on the server. In this example, calling GuiseSession.navigate("about") will cause navigation to occur to /server/webapp/example/about, while GuiseSession.navigate("/some/other/application/page") will navigate out of the Guise application entirely. Navigating to a URI, such as with GuiseSession.navigate(URI.create("http://www.example.com/about")), will transfer navigation to another server.

Navigation can me nonmodal as in the previous examples, in which subsequent navigation can occur to any other location. Guise also allows modal navigation using GuiseSession.navigateModal(String, ModalListener) or GuiseSession.navigateModal(URI, ModalListener). Modal navigation will only work properly if the navigation panel bound to the destination navigation path is an instance of ModalNavigationPanel Once modal navigation is initiated, subsequent navigation to other navigation panels within the application is not permitted until the destination navigation panel calls GuiseSession.endModalNavigation(ModalNavigationPanel). (The sole exception is if the desination navigation panel itself requests modal navigation to another modal navigation panel; modal navigation in Guise can be nested.) Once modal navigation ends, the navigation panel's modal result is available via ModalNavigationPanel.getResult(), and any ModalListener specified when modal navigation began will be notified.

Within Guise, most dialog-type activity should be ModalNavigationPanels. This prevents browser forward/back functionality from creating disparities between UI appearance and dialog state, and also causes the modal navigation panels automatically to be released when modality ends, so that new modal navigation will start with a newly initialized dialog.

Bookmarks

Guise can keep track of a bookmark at each navigation path, which is equivalent to a URI's query parameters. Bookmarks allow the state of a navigation panel to be referenced by a URI, and allow the back button to function as expected on browsers. Whenever an application wishes to indicate that a particular state of a navigation panel (for example, the currently selected tab in a tabbed panel) can be referenced by a bookmark, the application should publish the bookmark by calling GuiseSession.setBookmark(Bookmark). This does not necessarily cause navigation to occur, but indicates that a particular state may be referenced via bookmark information.

Whenever a user navigates to a bookmarked location (either by entering the URI in the browser manually or by pressing the back/forward buttons, for example), all components at the given destination that implement NavigationListener will be notified of the navigation. Each such component should override NavigationListener.navigated(NavigationEvent) and set the appropriate state of its child components, based upon the given bookmark.

Resources

While it is possible to hard code strings and other resources directly into Guise application code, Guise provide a preferred internationalized resource system that is simple to use and extensive. This system is divided into two levels:

Resource Bundles
Each Guise session uses a resource bundle with strings, integers, booleans, and other data types. This resource bundle is created independently for each Guise session based upon that session's locale. Whenever a session changes locales, the resource bundle is reloaded.
Resource Files
Each Guise session has available resources stored in resource files. On the Guise web platform, these files are stored in the WEB-INF/guise/resources/application directory. For example, for a Guise application mapped to /server/webapp/example/, file resources are stored in the /server/webapp/WEB-INF/guise/resources/example/ directory.

When a resource is requested using a resource key, the Guise session first looks to see if the resource bundle contains the requested resource. If a string resource cannot be found in the resource bundle, and if the resource key is a relative path, Guise searches for the resource in the resource file area. Within the resource file area, Guise automatically loads a resource with a filename based upon the current locale. That is, if a resource of my/resource.html was requested from a Guise session using the fr locale, Guise would first try to locate the file /server/webapp/WEB-INF/guise/resources/example/my/resource_fr.html and, if that resource did not exist, would return the contents of the resource /server/webapp/WEB-INF/guise/resources/example/my/resource.html.

As an example, assume that a Guise application specifies a resource bundle using GuiseApplication.setResourceBundleBaseName("myresources"). If a Guise session is created using the fr locale (or if a Guise session later changes to that locale), the Guise session attempts to load the resource bundle "myresources_fr.properties" using the Guise application's class loader. If that file cannot be found, an attempt is made to load the "myresources.properties" resource bundle. If a resource bundle is used, it must be included in the WEB-INF/classes/ directory or otherwise bundled with the application; the same is true of any resource files used.

The Guise session can thereafter request resources through one of the several GuiseSession.getXXXResource(String) methods. More commonly, a component is configured to automatically request resources when needed by providing the component with a resource key. For string properties a resource is referenced by surrounding the resource key with the Start of String control character (U+0098) and the String Terminator control character (U+009C) in the form "\u0098resourceKey\009C"—or as a convenience by calling Resources.createStringResourceReference(String). For URI properties a resource is referenced using the URI resource scheme in the form resource:resourceKey—or as a convenience by calling Resources.createURIResourceReference(String)

A Guise panel using a localized Label component, for instance, might request that the label use a string from the resource bundle stored under the key "my.label" by calling myLabel.setLabel(Resources.createStringResourceReference("my.label")) or myLabel.setLabel("\u0098my.label\u009C"). The code might specify that the label's icon URI be loaded from the resource bundle stored under the key "my.icon" by calling myLabel.setIcon(Resources.createURIResourceReference("my.icon")) or myLabel.setIcon(URI.create("resource:my.icon")). A Text component might load a localized XHTML resource using the following code:

final Text text=new Text();
text.setTextContentType(XHTML_CONTENT_TYPE);
text.setText(Resources.createStringResourceReference("my/localized/text.html"));

Internationalization

Guise is fully internationalized. It supports multiple localized resources stored in resource bundles and/or resource files. Every component also supports an Orientation, which specifies the physical axes of the Flow. Each flow (Flow.LINE and Flow.PAGE) has a flow direction, either increasing or decreasing from a standard origin in the top, left-hand corner. A Guise session automatically chooses component orientation based upon the session locale, but this can be manually overriden by application code.

In an Arabic locale of ar, for instance, all components would automatically be given an orientation in which line flow occured on the X axis decreasing from right to left, and page flow occurred on the Y axis increasing from top to bottom. Guise allows locale and/or component orientation to be changed at any time, and on the Guise web platform the XHTML will automatically reflect the language in the XHTML and XML, along with the component orientation in the XHTML.

Declarative Markup

Guise supports declarative descriptions of component hierarchies encoded in XML using URF PLOOP stored in TURF. For specific information on the URF PLOOP, one should consult the URF Specification, which contains a complete PLOOP eample of a Guise component specified by URF PLOOP in TURF.

Guise components may load their definitions from a PLOOP description document in one of several ways:

In all of these methods, the Guise session will lastly call Component.initialize() so that the component may install any event handlers or perform any other initialization. The component may wish to retrieve references to created comopnents using CompositeComponent.getComponentByName(String), which will search the hierarchy under the component for the first child with a matching name property, which would have been assigned in the PLOOP description document.