UIX Developer's Guide |
![]() Contents |
![]() Previous |
![]() Next |
This chapter presents the core visual component of UIX: the page. It describes what "page" means in UIX, how to define pages, and how to render them as user interfaces. This chapter contains the following sections:
On the web, the concept of "page" can be fairly straightforward. The simplest pages are collections of HTML elements that are interpreted and rendered in a web browser. However, in the context of web application development, the concept of "page" can be more complex, depending on how the page is defined and rendered and depending on the device(s) used to present it.
In UIX, a page is the logical representation of a unit of an application's user interface. It is a logical representation because it defines many of the characteristics of the unit--such as the parts of a visible page and the relationships among them--but it does not necessarily define exactly how the page will be presented. For example, the same logical page can be presented as an HTML page, as a WML (Wireless Markup Language) page, or via a voice interface.
A UIX page is only one unit of a user interface, because an application generally consists of multiple units of related parts, each of which can be represented as a separate page. For example, a page might represent one step in an extended flow of tasks.
These distinctions are important in UIX, because different UIX technologies are available for developing different aspects of a web application. The primary UIX technology for creating pages is UIX Components, which is described in this chapter. The technology for managing application flow among pages is UIX Controller, which is covered in depth in the UIX Controller chapter. You can use these technologies by themselves if you only need the functionality provided by one or the other, or you can use them together to simplify development of all these aspects of a web application.
In UIX, a logical page consists of a hierarchical set of components known as user interface nodes. Some nodes define visible components, such as buttons, images, tables, and text fields, while others organize the layout and appearance of other nodes and may also manage their behavior. UIX provides many common nodes, but it is also possible to define and create custom nodes.
Nodes can have parents and children, and multiple nodes together form a tree-like structure used to represent a page's logical structure. For example, Figure 3-1 shows a logical hierarchy of user interface nodes. Figure 3-2 shows that hierarchy rendered as an HTML page. (For the sake of simplicity, this illustration leaves out some details of how to define this page. See About Layouts, Children, and Roles, below, for a more information about what is missing. Also see Example of Creating a User Interface Node Tree to see the actual code.)
Figure 3-1: Logical hierarchy of Nodes in a UIX Page
Figure 3-2: Visual Hierarchy of Nodes in a UIX Page
A note about terminology: All visual UIX user interface components, such as buttons and tabs, are user interface nodes. However, UIX provides other user interface nodes that are not familiarly understood to be user interface components. For example, the stackLayout
node controls how its child nodes (such as the text
and image
user interface components) are laid out, but it is not itself visible. Therefore, some sections in this Guide use "user interface component" and "user interface node" interchangeably, while other sections talk about one or the other, depending on the context. "User interface node" is generally reserved for discussions about the nodes that comprise a logical page. "User interface component" is generally reserved for discussions about visible user interface components. When user interface nodes or components are discussed in a Java context, the terms "UINode
" and "bean" may be used. See Creating User Interface Nodes in Java, below, for more information about these Java terms.
While user interface nodes differ depending on their function, they share some common characteristics, described below.
User interface nodes are uniquely typed by two properties: name and namespace. Nodes of the same type share the same functionality as all other nodes of that type, of which there can be many. For instance, all button
nodes are the same type, but there can be many buttons.
The name property is self-explanatory: button
nodes all share the name "button"
, and image
nodes all share the name "image"
. But since you or other developers might define nodes with similar names, a mechanism is needed to make sure that all these names don't collide. That mechanism is namespaces, and it is part of the XML standard.
In short, a namespace is a unique string that is used to partition names into groups, or "spaces," whose members are unique. Each developer who defines user interface nodes must first define a unique namespace string. To ensure they are unique, the namespace strings are often chosen to be URLs that are owned by the developer defining the namespace. For instance, the UIX Components namespace is the string http://xmlns.oracle.com/uix/ui
. That string was chosen by the UIX development team, because they know they are the only developers who could own that URL. There is no requirement that there be anything actually sitting at that URL or that the URL is accessible to the user. The choice of an URL is simply a convenient way to assure uniqueness.
The namespace and the name of an element in that namespace together form a unique
pairing. A node named button
in the UIX Components namespace (http://xmlns.oracle.com/uix/ui
) is one such pairing. As such, it can be distinguished
from the button
node in any other namespace (such as http://www.example.org
). Thus, developers can define elements without worrying about name collisions.
Namespaces are a standard XML technology. For more detailed information, consult the World Wide Web Consortium web site at http://www.w3.org.
Aside from being uniquely identified by type, user interface nodes also have attributes or properties that describe a specific node instance. For example, a button node has attributes describing the text of the button, the destination the user might be taken to when the button is activated, and whether or not the button is enabled for use.
As previously mentioned, nodes can have children (but are not required to do so). While it might not make sense for a button to have any children, a header component might have children representing all the nodes grouped under its heading.
Another core characteristic of a node is that it can be rendered. That is, a node—which is a logical entity—can generate output that will represent it to a user, such as HTML code. The mechanics behind rendering are discussed later in this chapter.
UINode
Java InterfaceUIX includes Java classes corresponding to user interface nodes. These classes implement the base UINode
interface in the oracle.cabo.ui
package. The UINode
interface contains methods corresponding to the characteristics already described, such as:
public String getNamespaceURI();
public String getLocalName();
public int getIndexedChildCount(RenderingContext context);
public UINode getIndexedChild(RenderingContext context, int childIndex);
public Enumeration getAttributeNames(RenderingContext context);
public Object getAttributeValue(RenderingContext context, AttributeKey attrKey);
public void render(RenderingContext context)
throws IOException;
These UINode
methods, and a few others not yet listed, are covered in more detail later on. However, it is worth noting that this interface is immutable, or read-only. This means that while UINode
s must allow their names, namespaces, attributes, and children to be accessed, they cannot be changed through this interface. This immutability helps UIX work better in a multi-threaded server environment.
Many (but not all) of the elements in a uiXML document are transformed into user interface nodes when the document is processed by UIX. The other elements are used to support the user interface node elements, such as to specify the data they will receive.
Here is an example of a uiXML element that defines a simple button:
<button xmlns="http://xmlns.oracle.com/uix/ui" text="OK"/>
where:
button
is the name of the uiXML element. It defines a button
user interface component (node).
xmlns="http://xmlns.oracle.com/uix/ui"/
identifies the namespace of that button component. More precisely, "http://xmlns.oracle.com/uix/ui"/
is the value of the xmlns
attribute on the button
element.
text="OK"
declares a text attribute on the button
element. The value of the attribute is "OK," which is the text that will be rendered in the button.
UIX provides many high-level user interface nodes that create advanced user interface components when a page is rendered. But the most basic UIX documents do not need to have any high-level nodes in them, because UIX provides simple nodes--the text node and various HTML nodes--for the lowest-level page content. The text node creates text in a page, and the HTML nodes allow HTML to be directly rendered into a page. Here is an example of using one of these simple nodes to create a simple uiXML document:
<text xmlns="http://xmlns.oracle.com/uix/ui"
text="hello, world"/>
Here are a few things to note about this document:
The root element of the document, a <text>
element, is also the only element
in the document.
The xmlns="http://xmlns.oracle.com/uix/ui"
attribute on the
<text>
element tells UIX that this element, and any sub-elements inside
it, will default to being in the namespace "http://xmlns.oracle.com/uix/ui"
. This
is the UIX Components namespace described earlier. Namespaces can be said to cascade, which means that elements which do not declare their own namespace adopt the namespace of their closest ancestor element.
The <text>
element contains one attribute, text="hello, world"
which indicates to UIX what the text should actually be.
The following example shows uiXML that creates a page with some rudimentary HTML elements in it. (Note that the indented formatting is used to clarify that elements are children of other elements, but is not required.)
<div xmlns="http://www.w3.org/TR/REC-html40">
Hello world.
<ul>
<li>First HTML list element</li>
<li>Second HTML list element</li>
</ul>
</div>
Here is what is new:
New user interface nodes are used in this page. The <div>
element
is a standard HTML DIV element which groups its children in a block. It serves as the root
node here, and it contains text and some additional
HTML user interface nodes. These <ul>
and <li>
elements cause the generated page to include an HTML unordered list with two list items.
A different namespace declaration is used in the root <div>
element, which
describes the HTML namespace xmlns:html="http://www.w3.org/TR/REC-html40"
.
In the UIX page rendering framework, any element declared in the HTML namespace
will be "passed through" directly to the output when rendered, after having its
text transformed, or "escaped" by UIX to make it conform to HTML text rules.
Notice that that <li>
element requires a corresponding </li>
element here, even though it does not according to HTML rules. This is because not
closing an element tag would violate the XML rules for a well-formed document. Thus,
HTML included in a UIX document must actually be XHTML, a form of HTML that is also
well-formed XML.
The above example shows the simplest user interface nodes declared in a UIX page--text and HTML--but the interesting content comes when higher-level nodes provided by UIX Components are added. The following shows one of the most basic UIX Components nodes, a button, added to the example:
<div xmlns="http://www.w3.org/TR/REC-html40"
xmlns:ui="http://xmlns.oracle.com/uix/ui">
Hello world.
<ul>
<li>First list element</li>
<li>Second list element</li>
</ul>
<ui:button text="Push me" destination="http://www.example.org"/>
</div>
Here's what is new:
A second namespace declaration has been added in the root <div>
element. It is the UIX Components namespace "http://xmlns.oracle.com/uix/ui"
. Further, :ui
has been added to the end of the name of the xmlns
attribute, that is, xmlns:ui
. While the first namespace declared on the <div>
is still the default for it and its enclosed elements, any enclosed element that requires the UIX Components namespace can use it simply by prefixing ui:"
to its name, according to namespace rules. This is merely a convenience mechanism which allows elements to declare different namespaces easily further down in the document without a lengthy attribute.
<button>
element with two attributes, text
and destination
, has been added. These attributes describe two properties for the button. Note that it is in the UIX Components namespace, even though the other elements are in the HTML namespace.When this page is rendered, it will contain a visual button with the text "Push me" on it, which will activate the URL http://www.example.org
when it is pushed.
The button is one of the simplest UIX Components user interface nodes available. Documentation on other, more complex nodes is included later in this Guide.
While it is easy to create user interface nodes with uiXML, you can also create them directly with Java code. Every UIX release contains classes that can be used to create UINode
s. (Remember, UINode
is the Java interface used to define user interface nodes.) All of these classes reside in the oracle.cabo.ui.beans
package and its subpackages, with a few exceptions: the simple text node resides in oracle.cabo.ui.TextNode
, and the HTML UINode is located at oracle.cabo.ui.html.HTMLWebBean
. To be more consistent with the Java terminology, most classes implementing the UINode interface in Java are referred to as "beans," in reference to the
JavaBeans standard for discoverable code units.
To create UINodes in Java, you will generally construct an instance of a class representing the type of node you wish to create; for example, to create a button node, you will create an instance of a ButtonBean
. To set attributes on the node, you should call any of the available "set" methods available on the bean which map to the attribute you wish to set. For instance, the following code
shows the previous example page's content area created in Java instead of UIX:
import oracle.cabo.ui.TextNode;
import oracle.cabo.ui.html.HTMLWebBean;
import oracle.cabo.ui.beans.nav.ButtonBean;
// ... skipping class declaration
HTMLWebBean div = new HTMLWebBean("div");
// the text node is the first child of the "div"
TextNode helloText = new TextNode("hello, world");
div.addIndexedChild(helloText);
// the unordered list is next
HTMLWebBean unorderedList = new HTMLWebBean("ul");
div.addIndexedChild(unorderedList);
// the unordered list gets two list item children
// note that the list item text is added as text nodes here
HTMLWebBean firstListItem = new HTMLWebBean("li");
firstListItem.addIndexedChild(new TextNode("First list element"));
HTMLWebBean secondListItem = new HTMLWebBean("li");
secondListItem.addIndexedChild(new TextNode("Second list element"));
unorderedList.addIndexedChild(firstListItem);
unorderedList.addIndexedChild(secondListItem);
// finally create the UIX Components ButtonBean with our two attributes
ButtonBean button = new ButtonBean();
button.setText("Push me");
button.setDestination("http://www.example.org");
div.addIndexedChild(button);
As the next section shows, the result is the same, whether uiXML or Java code is used to create the nodes.
User interface nodes are the basic building blocks used to create a UIX page. The page itself is created by building a hierarchical "tree" of nodes. The remainder of this chapter discusses how to build such a tree (which represents a logical page) and render it as physical output.
Creating hierarchies of nodes is possible because many nodes can be parents to other nodes, which are then considered their children. An example earlier in this chapter showed the <button>
element (which is a node) as a child of the <div>
element. By making child nodes themselves parents of other nodes, more complex structures can be formed. Not every user interface node allows other nodes to become its children, however; this is largely dependent on the type of nodes in question. For instance, buttons in logical pages would not normally allow other content inside them, consequently the button node does not accept children, either.
Although some user interface nodes don't accept any child nodes, other nodes are designed specifically for managing a set of child nodes. Such nodes are often referred to as "layout nodes." Layout nodes may or may not cause any visible output on a generated page themselves, but they always manage the placement of any child nodes they contain. One common example of a layout node is a "stack layout" node, which takes multiple child nodes and lays them out in a vertical row from top to bottom on the page generated for the user. Other examples of layout nodes are discussed in the reference documentation for the UIX Components uiXML elements.
User interface node trees don't enforce that a given node has only one parent. Nodes can be added as children of other nodes at multiple places in the same tree, thereby allowing one section of a page's description to be easily replicated in another portion of the tree. This can be useful if you have identical content in multiple places in the same page.
No matter how your node tree is defined, the result is the same: a set of Java objects residing on the server. Each of these objects is a oracle.cabo.ui.UINode
. The actual tree of Java objects is created in one of the following ways:
If you have specified your tree using uiXML, the uiXML document must be transformed into a tree of Java objects. There are two easy ways to do this:
If you are using the UIX Controller framework to manage your application flow, UIX Controller contains utility classes that automatically transform your UIX document into a tree of Java objects.
If you are managing your own application flow without UIX Controller, but you need
to transform your uiXML document to a tree of Java objects, you can use
the utility class oracle.cabo.ui.xml.UINodeUtils
. This
class has a method (createUINode()
) which takes a
Java File
, or a Java InputSource
from the
commonly used SAX library, and will create a Java object tree based
on the UIX in that input.
If you have built your tree of nodes using the Java beans classes, then no
transformation is needed, as the beans already exist as Java classes which implement
the UINode
Java interface. Your tree is ready once you have compiled
and run the code which creates it.
The entry point for any tree is referred to as the "root" node. It is the
highest parent in the node tree hierarchy, and it is also the UINode returned as a
result of the UINodeUtils.createUINode()
method. But most important, it is the node at which the rendering process starts, which is how a tree is used to generate output for an end user. Once a UINode tree is created, it should be kept in memory for the lifetime of the entire application. The UIX
technology stack is designed to reuse the same tree for as many page renderings
as is needed without having to recreate or alter it. Doing so reduces the performance
cost for using UIX Components pages to generate a user interface. More information on the reuse
of node trees is provided in this and subsequent chapters.
User interface nodes that manage the position of their children in a page are called layout nodes (or layout components, layout elements, or simply layouts, depending on the context). Examples include stackLayout
, borderLayout
, pageLayout
, and table
. UIX provides two primary ways to arrange children in a layout. In the first, the children are simply laid out one after another in a sequence. In the second, the children are placed in certain predetermined positions, for example, top, bottom, left, right, and so forth. Children that are laid out in a sequence are called "indexed children." Children that are laid out in specific, named positions are called "named children." These terms are convenient ways to refer to children in those positions, but they don't actually refer to any inherent property of the children themselves. For example, you can arrange four images in a vertical row, as indexed children, or you can arrange them at the top, bottom, left side, and right side of a layout, as named children. The images are still just images, but they are called indexed children or named children to identify the role they have in the layout.
To define a layout in uiXML, you must use one or more elements called role elements to tell the layout how it should arrange its children. The role element is not a user interface node. Rather, it serves to mediate between the parent node and its child nodes. For example, here is a listing of embedded uiXML elements:
<borderLayout>
<top>
<image ... />
</top>
<left>
<text ... />
</left>
<right>
<text ... />
</right>
</borderLayout>
Those elements form this hierarchy:
Figure 3-3: Tree of uiXML Parent, Role, and Child Elements
Here is the node tree that is created:
Figure 3-4: Tree of UIX User Interface Nodes
There is no equivalent to roles in the UIX Java API. Children are placed in position directly using methods such as setTop()
, setBottom()
, and addIndexedChild()
.
Indexed children are used when a parent node needs to manage its child nodes based only on the order in which they are stored in the parent. Otherwise, the children are treated identically. An example of a node that supports indexed children is the stackLayout
node. The stack layout places its children in a vertical row from top to bottom, and the only information it needs to accomplish this is the order of the children it manages.
To specify indexed children in uiXML, insert a <contents>
element inside the parent element, then add the child elements inside <contents>
. The <contents>
element is the role element required for adding indexed children to a layout. It is also the only role element available for adding indexed children.
List the indexed child elements under <contents>
in the order in which they should be laid out. For example, the following uiXML document shows a stack layout managing three indexed button children:
<stackLayout xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<button text="Indexed child 1"
destination="http://www.example.org"/>
<button text="Indexed child 2"
destination="http://www.example.org"/>
<button text="Indexed child 3"
destination="http://www.example.org"/>
</contents>
</stackLayout>
Using Java code, the same indexed children can be added using the addIndexedChild()
method available on most beans.
import oracle.cabo.ui.beans.layout.StackLayoutBean;
import oracle.cabo.ui.beans.nav.ButtonBean;
// ... skipping class declaration
StackLayoutBean parentStack = new StackLayoutBean();
parentStack.addIndexedChild(new ButtonBean("Indexed child 1",
"http://www.example.org"));
parentStack.addIndexedChild(new ButtonBean("Indexed child 2",
"http://www.example.org"));
parentStack.addIndexedChild(new ButtonBean("Indexed child 3",
"http://www.example.org"));
Named children are used to create parent-child relationships that need more context than just the order of the child. For instance, a border layout node might lay out its children in regions like "top," "bottom," "left," and "right." Since the order of the children does not naturally imply which one would go in a particular region, these children must be added using roles to indicate their positions.
To specify named children in uiXML, insert the appropriate role element inside the parent and then insert the child element inside its role. While layout elements can contain only one <contents>
role (for adding indexed children) they can contain multiple roles to indicate different named roles (for adding named children). Only one direct child can be placed in any named role.
A border layout node with named text children in four regions would appear as follows. The role elements in this example are <top>
, <bottom>
, <right>
, and <left>
,
<borderLayout xmlns="http://xmlns.oracle.com/uix/ui">
<top>
<text text="Top text"/>
</top>
<bottom>
<text text="Bottom text"/>
</bottom>
<right>
Right text
</right>
<left>
Left text
</left>
</borderLayout>
Note that the order the named children are listed in the parent is irrelevant. Also in
this example, notice that text does not always have to be placed in a <text>
node; as long as the text to display is properly inside a role element, it can just
be entered directly into the uiXML file, as it is for the left and right text children.
Java code to accomplish the same takes advantage of the fact that beans will usually have special methods for setting named children:
import oracle.cabo.ui.TextNode;
import oracle.cabo.ui.beans.layout.BorderLayoutBean;
// ... skipping class declaration
BorderLayoutBean parentBorder = new BorderLayoutBean();
parentBorder.setTop(new TextNode("Top text"));
parentBorder.setBottom(new TextNode("Bottom text"));
parentBorder.setRight(new TextNode("Right text"));
parentBorder.setLeft(new TextNode("Left text"));
Some layouts support both indexed and named children. The named children are laid out in their named locations, and the indexed children are laid out sequentially in the location appropriate for the layout. In the following example, the images are named children and are placed on the top, bottom, left side, and right side of the layout. The text strings are indexed children and are laid out left to right, in order, in the middle of the layout.
<borderLayout> <top> <image source="images/globalhelp.gif"/> </top> <bottom> <image source="images/warnl.gif"/> </bottom> <end> <image source="images/cobrand.gif"/> </end> <start> <image source="images/info.gif"/> </start> <contents> <text text="This is the first sentence. " /> <text text="This is the second sentence. " /> <text text="This is the third sentence. " /> </contents> </borderLayout>
Note: Why does UIX provide different means for identifying indexed and named children? Without named children, the only way to specify that a child should be placed at "right," for example, would be to choose an index number which stood for "right." Using recognizable names makes it more intuitive. Without indexed children, a predetermined name would have to be given to every child in a sequence, and that number could be boundless.
This section presents a somewhat more extensive example of what is required to translate a visual page design to a tree of nodes. This example builds on the illustrations presented as Figure 3-1 and Figure 3-2.
First, it is important to study the desired layout of the target design and partition it into a logical node tree. Figure 3-5 shows a design containing a few "header" nodes for grouping content and some sample text and images that they might contain.
Figure 3-5: Visual Hierarchy of Nodes in a UIX Page
In Figure 3-6, the same hierarchy of that design has been logically structured as a clear tree of nodes. Once the node tree is determined, it is time to develop the code to create this tree.
Figure 3-6: Logical hierarchy Nodes in a UIX Page
The uiXML code to create such a tree follows. Remember, when you are using uiXML to define layouts with named or indexed children, you must use role elements to specify how to lay out the children. The following example shows only indexed children, so the role element <contents>
is used for each layout element.
<header xmlns="http://xmlns.oracle.com/uix/ui"
text="Header 1">
<contents>
<stackLayout>
<contents>
This is sample text. This is sample text.
<header text="Header 2">
<contents>
<stackLayout>
<contents>
This is more sample text, for the
purpose of the demonstration.
<image source="shapes.gif" shortDesc="Three Shapes"/>
</contents>
</stackLayout>
</contents>
</header>
</contents>
</stackLayout>
</contents>
</header>
The Java code to create the same node tree is as follows:
// -- Create a tree of UIX Components Beans --
// Create the first header.
HeaderBean header1 = new HeaderBean("Header 1");
// Create the text for the first header.
TextNode text1 =
new TextNode("This is sample text. This is sample text.");
// Create a stack layout for laying out the first header's
// children vertically.
StackLayoutBean stackLayout1 = new StackLayoutBean();
//Add the text as a child of the stack layout
stackLayout1.addIndexedChild(text1);
// Add the stack layout as the child of the fist header
header1.addIndexedChild(stackLayout1);
// Create the second, nested header
HeaderBean header2 = new HeaderBean("Header 2");
// Create the contents of the second header - some text and an image.
TextNode text2 =
new TextNode("This is more sample text, for the purpose of the
demonstration.");
ImageBean image = new ImageBean("shapes.gif", "Three Shapes");
// Create a stack layout for laying out the children of the second
// header.
StackLayoutBean stackLayout2 = new StackLayoutBean();
// Add the children to the second stack layout, and add the stack
// layout as the child of the second header.
stackLayout2.addIndexedChild(text2);
stackLayout2.addIndexedChild(image);
header2.addIndexedChild(stackLayout2);
// Add the second header as a child of the first stack layout (which
// is the child of the first header).
stackLayout1.addIndexedChild(header2);
Once a node tree is defined and created on a server, the tree must be rendered to a client so users can actually see (or hear) it. The UIX renderers can be used to render the node tree into HTML, WML, or some other form of output that an end user can experience.
Usually, the output sent to a client is much larger than the amount of UIX code used to specify a node tree. For example, UIX Components might render a brief UIX code segment as a complex group of HTML elements. This is one of the features of UIX: it transforms simple specifications into complex output code, and it hides this complexity from you, the developer.
To render a node tree that exists on a server, you must call the render()
method on the root node of that tree, and you must pass it a RenderingContext
:
UINode.render(RenderingContext context)
To do that, you must first create a RenderingContext
and configure it properly.
RenderingContext
The oracle.cabo.ui.RenderingContext
is a class used on the server to provide any stateful information to UIX Components for one render of a node tree. A node tree is created once and used on the server
for the life of an application. The RenderingContext
encapsulates all the per-render information that is short-lived.
The typical page rendering process is as follows:
RenderingContext
based on information in that request.RenderingContext
to the root node's render(RenderingContext context)
method.RenderingContext
is discarded.Once the render(RenderingContext context)
method is called on the root node, the internal UIX Components renderer classes take over and do the work necessary to generate output. That process will be described more fully later in this chapter.
If you decide to use UIX Controller for managing your application flow, it will automatically create a RenderingContext
for each request and use it to render the node tree for a page. If you do not use UIX Controller, you must perform these steps yourself or use the UIX test/demo classes during your debugging. One helpful class is oracle.cabo.ui.test.UIXTest
, a Java application that takes the path to a .uix
file as an argument and will open a browser window displaying the rendered result of that file.
However, if your development tasks are focused only on page creation, you may safely skip to the next chapter of this guide.
RenderingContext
MethodsThe RenderingContext
class essentially serves as a repository for other contexts and properties that will be needed during a page render. Many are described here, but there are additional methods that are either used only by the internal UIX Components code or that will be covered in chapters describing advanced UIX Components topics. Here is a summary of the RenderingContext
methods discussed later in this chapter:
public interface RenderingContext
{
// Some methods not listed...
// ...
public OutputMethod getOutputMethod() throws IOException;
public Agent getAgent();
public LocaleContext getLocaleContext();
public ErrorLog getErrorLog();
public DataObject getDataObject(String namespaceURI, String name);
public RendererManager getRendererManager();
// ...
}
OutputMethod
Every RenderingContext
must supply to the UIX rendering code a destination for the generated output for that render. The oracle.cabo.ui.io.OutputMethod
serves this purpose, as well as others.
The OutputMethod
is an interface which the UIX rendering code utilizes when it generates output for one render. When created, an OutputMethod
implementation is connected to some output destination, such as a web server response stream or a file on a file system. Then, any UIX renderer (for instance, the renderer which generates output for a button node) will access the OutputMethod
from the RenderingContext
and call the methods on it to write elements, comments, and text to that stream or file.
Some of the core OutputMethod
s perform special handling and text escaping that makes sure that any generated output is suitable for the destination. Examples of this are the oracle.cabo.ui.io.HTMLOutputMethod
and the
oracle.cabo.ui.io.XMLOutputMethod
, which adjusts output from the renderers to make sure that they comply with the formatting rules of HTML and XML, respectively.
Other OutputMethod
s serve only as utilities and are intended to wrap the core implementations:
The oracle.cabo.ui.io.DebugOutputMethod
class can be created
to wrap another OutputMethod
and provide additional debugging feedback,
such as warnings when nodes are used improperly or the renderer calls are not
balanced.
The oracle.cabo.ui.io.PrettyPrinter
class is also created
to wrap another OutputMethod
, but its function is to make the
output easier to read by nesting and indenting the generated elements.
Both of these utility OutputMethod
s can be used at development time
and then simply removed when an application is deployed on a production server,
in order to avoid any associated performance penalty.
Here is some sample code that creates wrapped OutputMethod
s
which will generate the result to an existing Java PrintWriter
:
public OutputMethod createOutputMethod()
{
// supply a print writer which will receive the rendering output
PrintWriter myPrintWriter = getPrintWriter();
OutputMethod coreOutputMethod = new HTMLOutputMethod(myPrintWriter);
// wrap that core OutputMethod with utility methods if you desire
return new PrettyPrinter(new DebugOutputMethod(coreOutputMethod));
}
The oracle.cabo.share.agent.Agent
class stores all of the
information about the target device for which this render is intended. That
target can be a common web browser, a mobile device, or any other "agent"
supported by the UIX Components project.
Like the OutputMethod
, the Agent
interface is also
used by the UIX Components renderers when they are generating output. In order to provide
the best rendering results, the UIX Components renderers commonly adjust the output they
generate based on the Agent
information they are given. For instance,
a complex data table node will automatically be rendered in a more simple manner
for less powerful browsers than for full-featured browser, and the quirks of the
target platform can be taken into account.
Currently the Agent
can account for variations in:
However, it is up to the internal UIX Components rendering code to use this information if it is truly needed. For instance, many combinations of browsers and version numbers will probably render exactly the same.
The LocaleContext
is used by the internal UIX Components rendering
classes to adjust the content based on the locale for which the current
render is targeted. The most obvious example is that the UIX Components code
will translate any of its own content to match the specified locale, but
the LocaleContext
also provides the UIX Components renderers information
on the preferred date and time formats.
While the LocaleContext
will default most of its settings
based on the java.util.Locale
that is passed into it when
it is constructed, you can also override specific features of the locale if
you would like a particular render to use a different date format, for instance.
The ErrorLog
is very similar to the OutputMethod
,
in that the UIX Components renderers use it as a central interface for logging any
errors that they encounter when they try to render the page. UIX Components provides
implementations of the ErrorLog
interface that render to the
console (oracle.cabo.share.error.BaseErrorLog
) or to a servlet
error log (oracle.cabo.share.error.ServletErrorLog
).
The RenderingContext
also contains methods for storing
dynamic data that alters the contents of the node tree for a given render.
This is the mechanism that allows different content to be generated to
each user even though the node tree remains constant. See Data Binding for more information about this topic.
The two implementations of the RenderingContext
interface
provided by UIX Components are the oracle.cabo.ui.RootRenderingContext
and the oracle.cabo.ui.ServletRenderingContext
. Both of these
classes default much of the context information but also allow you to
customize the various subcontexts.
The RootRenderingContext
should only be instantiated when you need
complete control over the rendering information and you are not using a
Java servlet to manage your web application. By default, it initializes itself in the following way:
OutputMethod
cannot be defaulted, and must be passed
in at creation time or generated by overriding the createOutputMethod()
method. Without an OutputMethod
, the render cannot occur.Agent
is defaulted to an unknown HTML agent, unless a better
Agent is provided.LocaleContext
defaults to the locale information of the
server itself if better information is not provided.ErrorLog
will by default print any errors it finds
to Java's System.err
stream, however that is configured.However, you will probably always use the ServletRenderingContext
(or let UIX Controller configure one for you), especially if you are using a Java servlet to run your
web application or you are generating a Java Server Page (JSP). The
ServletRenderingContext
will default its contexts
off of the information it can gather from a ServletRequest
or a JSP PageContext
:
OutputMethod
will be based on the JSP output
stream if a PageContext
is provided, or you will need to
pass in a PrintWriter
if you are using a servlet. Without
an OutputMethod
, the render cannot occur.Agent
will be automatically configured based on the
properties of the request or context passed in.LocaleContext
will also be automatically configured
based on the request or context.ErrorLog
will be set to the appropriate error log
of the servlet.Finally, here is some sample code that sets up and renders a page based in
a Servlet
for a given request:
public void renderStartPage(
Servlet servlet,
ServletRequest request,
ServletResponse response,
PrintWriter out
) throws IOException
{
ServletRenderingContext context = new ServletRenderingContext(servlet, request, response, out);
UINode rootNode = getStartPageTree();
rootNode.render(context);
}
This section describes the process by which UIX Components transforms a UINode tree and a RenderingContext into output for the end user. The material given here is provided mostly for informational purposes and is not required knowledge to use the UIX Components page rendering framework.
Although rendering starts with a call to UINode.render()
, very little directly happens in
that method. Instead, the UINode
uses
Renderers
to write out its content. The
Renderer
class is a small Java interface
that is responsible for:
UINode
So, how is a UINode
attached to a Renderer
? Remember that UINodes
are identified by a pair of strings: a
namespace and a name. Not coincidentally, the UIX RendererManager
class can find a Renderer
by namespace and name, and RenderingContext
has a getRendererManager()
method. So, in the vast
majority of cases, all a UINode
does to
render is get the RendererManager
,
use it to get a Renderer
, then hand off
control to the Renderer
.
Once the Renderer
has control, it talks
to the UINode
to get attributes and
children as needed, and calls the OutputMethod
when it needs to write output. The
UINode
neither knows or cares what's in
the output; it is only responsible for handing attributes and children
to the Renderer
when asked.
At any point, a Renderer
may wish to
render a child UINode
. At this point, it
calls either UINode.getIndexedChild()
or
UINode.getNamedChild()
to get the child,
then calls UINode.render()
on that child.
The process begins anew with that child - it gets its own Renderer
, which gets grandchildren, etc.
This design has a number of implications:
Renderer
, you can
completely alter the behavior of all UINodes
with the same namespace and name.
RendererManager
,
you can completely alter the output of the entire page! This
means that you can reuse one UINode
tree to output with different look-and-feels, and to different
user agents: the same tree can output HTML tailored to both a
desktop web browser and a handheld device.
Renderers
control not only when
children render - but if they render at all! So,
simply adding an indexed child or a named child is no guarantee
that that child will appear. The Renderer
has to explicitly ask for that
child.
Renderer
never sees ButtonBean
, or LinkBean
, or any other bean class. Instead, it simply sees UINodes
. This in turn means that:
UINode
directly if you wish without
writing a new renderer.
Renderers
cannot set any
values on UINodes
. Rendering
a UINode
tree cannot change it!
Renderers
always ask child UINodes
to render themselves - instead of directly getting a Renderer
- UINodes
don't necessarily have to use a Renderer
. Still, it's usually much cleaner to keep rendering code in a separate class.
More of the UIX features for rendering pages are covered in later chapters.