用JSP和XML开发web应用程序
第三部分:开发JSP客户标签 原著:Qusay H. Mahmoud 翻译:李会民(水平有限,如有不妥之处请见谅) JavaServer PagesTM (JSPTM)技术使将Java代码嵌入HTML文档变得简单了。然而这个方案并不是适合所有的HTML内容开发者,或许是因为他们不懂Java和不想去学Java的语法。虽然JavaBeans能够用来将很多Java代码封装进去,但是在JSP中使用他们仍然需要内容开发者对Java的语法有一些了解。JSP 技术允许你通过标签库引入更多的新的定制的标签。作为一个Java的开发者,你能够通过引入定制的标签扩展JSP的页面。定制的标签也允许你通过业务逻辑与显示逻辑的分离提供更好的包装。   这篇文章展示了关于定制标签(tags)的一些看法,他们是: 如何开发和配置简单的标签 
如何开发和定制高级的标签:带参数的标签和标签的内容 
如何用Tag Library Descriptor (TLD)描述标签 
  最后,提供一些实例程序。 标签的总体看法 如果你有HTML方面的经验的话,你已经知道了能够使用的标签的类型。主要有两种标签,并且都可以有参数:  没有内容的标签:一个没有内容的标签是指指又开始符没有结束符的标签。语法如下:  
<tagName attributeName="value" 
anotherAttributeName="anotherValue"/>
没有内容的标签用来表现某几种特定的功能,例如描述一个input field,或者显示一附图。例子如下: <IMG SRC="./fig10.gif">   由内容的标签:由开始和结束符的标签,语法如下  
<tagName attributeName="value" 
anotherAttributeName="anotherValue">
...tag body...
</tagName>
由内容的标签用来执行执行动作,例如格式化。如下例: <H2>Custom Tags</H2>    JSP 自定义的标签
JSP定制的标签只是实现特殊接口的类。一旦被开发个配置后,可以从HTML中使用XML语法调用他们的动作。他们有开始和结束符。他们可能有或没有内容(body)。一个无内容的(bodyless)的标签可以表示如下: <tagLibrary:tagName /> 并且,一个有内容的(body)标签表达如下:  <tagLibrary:tagName>
   body
</tagLibrary:tagName>
另外,两种都可以拥有参数区定制一个标签的行为。线面的标签有一个属性name,他接收一个通过评估变量yourName的值得到得字符串值 : <mylib:hello name="<%= yourName %>" /> 或者,它可以写成如下形式: <mylib:hello>
  <%= yourName %>
</mylib:hello>
 定制标签的好处(Benefits of Custom Tags)  一个关于JSP定制标签得非常重要的注意事项:他们不能比scriptlets提供更多的功能,他们指示提供更好的包装,帮你更好的是业务逻辑和显示逻辑分离:好处如下:  能够减少或消除scripleys在你的JSP应用程序中。任何必要的参数都可以作为属性或内容传递,因此Java代码不是必需去初始化或设置属性 
拥有简单的语法。Scriptlets用Java写成,但是丁指标签能使用HTML_like 语法。 
能提高内容开发者的生产力。 
可复用 
简而言之,你能使用定制标签完成复杂的任务  定义标签(Defining a Tag) 
JSP定制的标签只是实现特殊接口的类。他用来封装功能。我们已经提到过,标签可以有或没有能容。如果要定义一个简单的无内容标签,你的class必须implement the Tag interface.开发有内容(body)标签后面讨论。例1是Tag 接口必须实现的代码: Sample 1: Tag.java   public interface Tag {
   public final static int SKIP_BODY = 0;
   public final static int EVAL_BODY_INCLUDE = 1;
   public final static int SKIP_PAGE = 5;
   public final static int EVAL_PAGE = 6; 
   void setPageContext(PageContext pageContext);
   void setParent(Tag parent);
   Tag getParent();
   int doStartTag() throws JspException;
   int doEndTag() throws JspException;
   void release();
}
所有的标签都必须实现Tah interface中的方法,表1提供了方法的描述 Table 1: Description of methods in the Tag interface 
 
Method 
 Description 
 
setPageContext(PageContext pc) 
 This method is invoked by the JSP runtime, prior to doStartTag, to set the page context. 
 
setParent(Tag parent) 
 Invoked by the JSP runtime, prior to doStartTag, to pass a tag handler a reference to its parent tag. 
 
getParent 
 Returns a Tag instance that is the parent of this tag. 
 
doStartTag 
 Invoked by the JSP runtime to prompt the tag handler to process the start tag for this instance. 
 
doEndTag 
 Invoked by the JSP runtime after returning from doStartTag. The body of the action may or may not have been evaluated, depending on the return value of doStartTag. 
 
release 
 Invoked by the JSP runtime to indicate to the tag handler to perform any cleanup necessary. 
 第一个标签 
现在,让我们来看一个例子。 这里有几个关于开发定制标签的步骤: Develop the tag handler 
Create a tag library descriptor 
Test the tag 
1. 开发Tag Handler 
一个tag handler 是在JSP执行时被JSP调用的对象,tag handler 的方法在不同的点在标签赋值过程中被执行的class调用。每一个tag handler必须实现一个专门的接口。 Sample 2: HelloTag.java   package tags; import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*; 
public class HelloTag implements Tag {
   private PageContext pageContext;
   private Tag parent; 
   public HelloTag() {
      super();
   } 
   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print(
         "This is my first tag!");
      } catch (IOException ioe) {
         throw new JspException("Error: 
         IOException while writing to client" 
         + ioe.getMessage());
      }
      return SKIP_BODY;
   } 
   public int doEndTag() throws JspException {
      return SKIP_PAGE;
   } 
   public void release() {
   } 
   public void setPageContext(PageContext 
   pageContext) {
      this.pageContext = pageContext;
   } 
   public void setParent(Tag parent) {
      this.parent = parent;
   } 
   public Tag getParent() {
      return parent;
   }
}
两个最主要的方法是doStartTag和doEndTag。DoStartTag在遇到开始符执行。着这个例子中,这个方法返回SKIP_BODY因为一个简单的标签没有body.doEndTah在遇到end tag是调用。在此例中返回SKIP_PAGE因为我们不想求(evaluate)余下页面的值;否则应该返回EVAL_PAGE。 编译此类,假设Tomcat装在c:\tomcat: 在下列目录创建tags目录c:\tomcat\webapps\examples\web-inf\classes. 
将HelloTag.java 存入此目录 
编译如下: 
c:\tomcat\webapps\examples\web-inf\classes\tags> 
javac -classpath c:\tomcat\lib\servlet.jar 
HelloTag.java

解决方案 »

  1.   

    2. 创建Tag Library Descriptor 
    下一步就是如何被JSP调用。可以通过创建Tag Library Descriptor (TLD)来完成,这是一个 XML文档. Sample 3: mytaglib.tld   <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE taglib
            PUBLIC "-//Sun Microsystems, Inc.//
            DTD JSP Tag Library 1.1//EN"
            "http://java.sun.com/j2ee/dtds/
            web-jsptaglibrary_1_1.dtd"> 
    <!-- a tag library descriptor --> 
    <taglib>
       <tlibversion>1.0</tlibversion>
       <jspversion>1.1</jspversion>
       <shortname>first</shortname>
       <uri></uri>
       <info>A simple tab library for the 
       examples</info> 
      <tag>
        <name>hello</name>
        <tagclass>tags.HelloTag</tagclass>
        <bodycontent>empty</bodycontent>
        <info>Say Hi</info>
      </tag>         
    </taglib>
    我们首先指定tag library 版本和JSP版本。<shortname>指出我们在JSP中如何引用tag library.<uri>作为你得tag library的唯一标志。 在这个TLD中,我们只有一个标签hello,它的类被指定为使用tagclass标签。然而,一个tag library能有许多标签。<bodycontent>告诉我们此标签舞内容(body);否则会有错误。另一方面,如果你想凭家(evaluate)一个标签的内容(body),值如下: tagdependent: meaning that any body of the tag would be handled by the tag itself, and it can be empty. 
    JSP: meaning that the JSP container should evaluate any body of the tag, but it can also be empty. 
    保存mytaglib.tld 到此目录: c:\tomcat\webapps\examples\web-inf\jsp. 
      

  2.   

    3. 测试(Test the Tag) 
    最后一步是测试我们已经开发的标签。为了使用此标签我们必须引用他,有三种方法: Reference the tag library descriptor of an unpacked tag library. 例如: 
    <@ taglib uri="/WEB-INF/jsp/mytaglib.tld" 
    prefix="first" %>
    Reference a JAR file containing a tag library.例如: 
    <@ taglib uri="/WEB-INF/myJARfile.jar" 
    prefix='first" %>
    Define a reference to the tag library descriptor from the web-application descriptor (web.xml) and define a short name to reference the tag library from the JSP. 
    使用此方法,打开此文件:c:\tomcat\webapps\examples\web-inf\web.xml 并且把下面的代码加入最后,他是 <web-app>:      <taglib>
           <taglib-uri>mytags</taglib-uri>
           <taglib-location>/WEB-INF/jsp/
           mytaglib.tld</taglib-location>
        </taglib>
    现在,写一个JSP并且使用第一种语法,例4: Sample 4: Hello.jsp   <%@ taglib uri="/WEB-INF/jsp/mytaglib.tld"
     prefix="first" %>
    <HTML>
    <HEAD>
    <TITLE>Hello Tag</TITLE>
    </HEAD> 
    <BODY bgcolor="#ffffcc"> 
    <B>My first tag prints</B>: 
    <first:hello/> 
    </BODY>
    </HTML>
    taglib用来告诉JSP在那里找到我们tag library的描述符,并且prefix 指定我在此库中如何引用此标签。到此JSP能够认识任何一个我们标签的用法。只要我们预先声明了<first:hello/>. 作为选择,你可以是用第二种方法或第三种方法,只要将sample 4中的相关语句用下面的替代。  <%@ taglib uri="mytags" prefix="first" %>
    基本上,我们已使用了mytags名字,他已经被加入了web.xml,去指示tag library.余下的例子这个引用将被用到。 如果你从browser请求hello.jsp时,结果如Figure 1. 
    Figure 1: First Custom Tag由于java提供了一个TagSupport类,因此一个写一个简单的tag简便的方法是extend TagSupport class. 你可以将TagSupport想象成adapter.Sample4能简单的用Sample5实现 Sample 5: Extending the TagSupport class package tags; 
    import java.io.*;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*; 
    public class HelloTag extends TagSupport { 
       public int doStartTag() throws JspException {
          try {
             pageContext.getOut().print("This is my 
             first tag!");
          } catch (IOException ioe) {
             throw new JspException("Error: 
             IOException while writing 
            to client" + ioe.getMessage());
          }
          return SKIP_BODY;
       } 
       public int doEndTag() throws JspException {
          return SKIP_PAGE;
       }
    }
    Parameterized Tags 
    我们已经看到了如何开发简单的标签。现在,让我们看看如何开发parameterized tags.两个新的东西需要添加到以前的例子中: Add a set method 
    Add a new tag to mytagslib.tld 
    在Sample5中添加一组方法并且更改输出结果 Sample 5: A tag with an attribute   package tags;
    import java.io.*;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;
    public class HelloTagParam extends TagSupport {
       private String name;   
       public void setName(String name) {
          this.name = name;
       }
       public int doStartTag() throws JspException {
          try {
             pageContext.getOut().print("Welcome to 
             JSP Tag Programming, " +name);
          } catch (IOException ioe) {
             throw new JspException("Error: 
             IOException 
             while writing to client");
          }
          return SKIP_BODY;
       }
       public int doEndTag() throws JspException {
          return SKIP_PAGE;
       }
    }
    下一件需要做的事是添加新tag到mytaglib.tld. 新tag显示在Sample 6中.这段代码应该添加到mytaglib.tld的最后一行</taglib>: 
      

  3.   

    PACKAGE COM.PSOL.VIEWXML;
    IMPORT JAVA.IO.*;
    IMPORT JAVA.AWT.*;
    IMPORT JAVA.AWT.EVENT.*;
    IMPORT JAVAX.SWING.*;
    IMPORT JAVAX.SWING.TREE.*;
    IMPORT ORG.W3C.DOM.*;
    IMPORT COM.IBM.XML.PARSER.*;
    CLASS VIEWXML EXTENDS JFRAME
    {
    PROTECTED DEFAULTTREEMODEL TREEMODEL;
    PROTECTED DEFAULTMUTABLETREENODE TREEROOT;
    PROTECTED DEFAULTLISTMODEL MESSAGEAREA;
    PUBLIC VIEWXML()
    {
    SETSIZE(NEW DIMENSION(400,300));
    SETTITLE("VIEWXML");
    CONTAINER CONTENTPANE = GETCONTENTPANE();
    SETJMENUBAR(CREATEMENU());
    TREEROOT = NEW DEFAULTMUTABLETREENODE();
    TREEMODEL = NEW DEFAULTTREEMODEL(TREEROOT);
    MESSAGEAREA = NEW DEFAULTLISTMODEL();
    CONTENTPANE.ADD(NEW JSPLITPANE(JSPLITPANE.VERTICAL_SPLIT,
    NEW JSCROLLPANE(NEW JTREE(TREEMODEL)),
    NEW JSCROLLPANE(NEW JLIST(MESSAGEAREA))));
    SETVISIBLE(TRUE);
    }
    PROTECTED JMENUBAR CREATEMENU()
    {
    JMENU FILEMENU = NEW JMENU("FILE"),
    HELPMENU = NEW JMENU("HELP");
    FILEMENU.SETMNEMONIC('F');
    HELPMENU.SETMNEMONIC('H');
    JMENUITEM OPENMENUITEM = NEW JMENUITEM("OPEN...",'O'),
    EXITMENUITEM = NEW JMENUITEM("EXIT",'E'),
    DEFAULTMENUITEM = NEW JMENUITEM("DEFAULT",'D'),
    ABOUTMENUITEM = NEW JMENUITEM("ABOUT VIEWXML...",'A');
    OPENMENUITEM.ADDACTIONLISTENER(NEW ACTIONLISTENER()
    {
    PUBLIC VOID ACTIONPERFORMED(ACTIONEVENT EVT)
    { DOOPEN(); }
    });
    EXITMENUITEM.ADDACTIONLISTENER(NEW ACTIONLISTENER()
    {
    PUBLIC VOID ACTIONPERFORMED(ACTIONEVENT EVT)
    { DOEXIT(); }
    });
    ABOUTMENUITEM.ADDACTIONLISTENER(NEW ACTIONLISTENER()
    {
    PUBLIC VOID ACTIONPERFORMED(ACTIONEVENT EVT)
    { DOABOUT(); }
    });
    ADDWINDOWLISTENER(NEW WINDOWADAPTER()
    {
    PUBLIC VOID WINDOWCLOSING(WINDOWEVENT EVT)
    { DOEXIT(); }
    });
    FILEMENU.ADD(OPENMENUITEM);
    FILEMENU.ADDSEPARATOR();
    FILEMENU.ADD(EXITMENUITEM);
    HELPMENU.ADD(ABOUTMENUITEM);
    JMENUBAR MENUBAR = NEW JMENUBAR();
    MENUBAR.ADD(FILEMENU);
    MENUBAR.ADD(HELPMENU);
    RETURN MENUBAR;
    }
    PUBLIC VOID DOOPEN()
    {
    CURSOR OLDCURSOR = GETCURSOR();
    TRY
    {
    FILEDIALOG FD = NEW FILEDIALOG(THIS);
    FD.SETMODE(FILEDIALOG.LOAD);
    FD.SHOW();
    IF(NULL != FD.GETFILE())
    {
    SETCURSOR(CURSOR.GETPREDEFINEDCURSOR(CURSOR.WAIT_CURSOR));
    TREEROOT.REMOVEALLCHILDREN();
    FILE FILE = NEW FILE(FD.GETDIRECTORY(),FD.GETFILE());
    ERRORLISTENER ERRORTOMESSAGEAREA = NEW ERRORLISTENER()
    {
    PUBLIC INT ERROR(STRING FNAME,INT LINENO,
    INT CHAROFF,OBJECT KEY,STRING MSG)
    {
    MESSAGEAREA.ADDELEMENT(FNAME + " (" + LINENO + ',' + CHAROFF + "): " + MSG);
    RETURN 1;
    }
    };
    PARSER PARSER = NEW PARSER(FILE.GETNAME(),ERRORTOMESSAGEAREA,NULL);
    DOCUMENT DOC = PARSER.READSTREAM(NEW FILEINPUTSTREAM(FILE.GETABSOLUTEPATH()));
    PROCESSDOCUMENT(TREEROOT,DOC);
    TREEMODEL.RELOAD();
    SETCURSOR(OLDCURSOR);
    }
    }
    CATCH(IOEXCEPTION E)
    {
    SETCURSOR(OLDCURSOR);
    JOPTIONPANE.SHOWMESSAGEDIALOG(THIS,
    E.GETMESSAGE(),
    "I/O ERROR",
    JOPTIONPANE.ERROR_MESSAGE);
    }
    }
    PUBLIC VOID DOEXIT()
    {
    SYSTEM.EXIT(0);
    }
    PUBLIC VOID DOABOUT()
    {
    JOPTIONPANE.SHOWMESSAGEDIALOG(THIS,
    "BY BENO&ICIRC;T MARCHAL FOR DIGITAL CAT\N" +
    "COPYRIGHT &copy; 1998, PINEAPPLESOFT SPRL\N" +
    "WWW.JAVACATS.COM\N" +
    "WWW.PINEAPPLESOFT.COM",
    "ABOUT VIEWXML",
    JOPTIONPANE.INFORMATION_MESSAGE);
    }
    PROTECTED VOID PROCESSNODE(DEFAULTMUTABLETREENODE TNODE,NODE NODE)
    {
    SWITCH(NODE.GETNODETYPE())
    {
    CASE NODE.ATTRIBUTE_NODE:
    PROCESSATTRIBUTE(TNODE,(ATTR)NODE);
    BREAK;
    CASE NODE.CDATA_SECTION_NODE:
    PROCESSCDATASECTION(TNODE,(CDATASECTION)NODE);
    BREAK;
    CASE NODE.COMMENT_NODE:
    PROCESSCOMMENT(TNODE,(COMMENT)NODE);
    BREAK;
    CASE NODE.DOCUMENT_NODE:
    PROCESSDOCUMENT(TNODE,(DOCUMENT)NODE);
    BREAK;
    CASE NODE.DOCUMENT_FRAGMENT_NODE:
    PROCESSDOCUMENTFRAGMENT(TNODE,(DOCUMENTFRAGMENT)NODE);
    BREAK;
    CASE NODE.DOCUMENT_TYPE_NODE:
    PROCESSDOCUMENTTYPE(TNODE,(DOCUMENTTYPE)NODE);
    BREAK;
    CASE NODE.ELEMENT_NODE:
    PROCESSELEMENT(TNODE,(ELEMENT)NODE,FALSE);
    BREAK;
    CASE NODE.ENTITY_NODE:
    PROCESSENTITY(TNODE,(ENTITY)NODE);
    BREAK;
    CASE NODE.ENTITY_REFERENCE_NODE:
    PROCESSENTITYREFERENCE(TNODE,(ENTITYREFERENCE)NODE);
    BREAK;
    CASE NODE.NOTATION_NODE:
    PROCESSNOTATION(TNODE,(NOTATION)NODE);
    BREAK;
    CASE NODE.PROCESSING_INSTRUCTION_NODE:
    PROCESSPROCESSINGINSTRUCTION(TNODE,(PROCESSINGINSTRUCTION)NODE);
    BREAK;
    CASE NODE.TEXT_NODE:
    PROCESSTEXT(TNODE,(TEXT)NODE);
    BREAK;
    DEFAULT:
    JOPTIONPANE.SHOWMESSAGEDIALOG(THIS,
    "FOUND A NODE OF UNKNOW TYPE.\N" + 
    "YOU SHOULD UPGRADE TO THE LATEST VERSION!",
    "VIEWXML",
    JOPTIONPANE.ERROR_MESSAGE);
    }
    }
    PROTECTED VOID PROCESSATTRIBUTE(DEFAULTMUTABLETREENODE TNODE,ATTR ATTR)
    {
    DEFAULTMUTABLETREENODE SUBTNODE =
    NEW DEFAULTMUTABLETREENODE(ATTR.GETNAME() + "=\"" + ATTR.GETVALUE() + '"');
    TNODE.ADD(SUBTNODE);
    }
    PROTECTED VOID PROCESSCDATASECTION(DEFAULTMUTABLETREENODE TNODE,CDATASECTION SEC)
    {
    DEFAULTMUTABLETREENODE SUBTNODE =
    NEW DEFAULTMUTABLETREENODE("[CDATA[" + SEC.GETDATA() + "]]");
    TNODE.ADD(SUBTNODE);
    }
    PROTECTED VOID PROCESSCOMMENT(DEFAULTMUTABLETREENODE TNODE,COMMENT COMM)
    {
    DEFAULTMUTABLETREENODE SUBTNODE =
    NEW DEFAULTMUTABLETREENODE("<!--" + COMM.GETDATA() + "-->");
    TNODE.ADD(SUBTNODE);
    }
    PROTECTED VOID PROCESSDOCUMENT(DEFAULTMUTABLETREENODE TNODE,DOCUMENT DOC)
    {
    ELEMENT ROOT = DOC.GETDOCUMENTELEMENT();
    IF(NULL != ROOT)
    PROCESSELEMENT(TNODE,ROOT,TRUE);
    }
    PROTECTED VOID PROCESSDOCUMENTFRAGMENT(DEFAULTMUTABLETREENODE TNODE,DOCUMENTFRAGMENT DF)
    {
    NODELIST NL = DF.GETCHILDNODES();
    INT SIZE = NL.GETLENGTH();
    FOR(INT I = 0;I < SIZE;I++)
    PROCESSNODE(TNODE,NL.ITEM(I));
    }
    PROTECTED VOID PROCESSDOCUMENTTYPE(DEFAULTMUTABLETREENODE TNODE,DOCUMENTTYPE DTD)
    {
    // DOES NOTHING AS WE DON'T BOTHER WITH DTD
    }
    PROTECTED VOID PROCESSELEMENT(DEFAULTMUTABLETREENODE TNODE,ELEMENT EL,BOOLEAN ISROOT)
    {
    DEFAULTMUTABLETREENODE SUBTNODE;
    IF(ISROOT)
    {
    TNODE.SETUSEROBJECT("<" + EL.GETTAGNAME() + ">");
    SUBTNODE = TNODE;
    }
    ELSE
    {
    SUBTNODE = NEW DEFAULTMUTABLETREENODE("<" + EL.GETTAGNAME() + ">");
    TNODE.ADD(SUBTNODE);
    }
    NAMEDNODEMAP ATTRMAP = EL.GETATTRIBUTES();
    INT SIZE = ATTRMAP.GETLENGTH();
    FOR(INT I = 0;I < SIZE;I++)
    PROCESSATTRIBUTE(SUBTNODE,(ATTR)ATTRMAP.ITEM(I));
    NODELIST NL = EL.GETCHILDNODES();
    SIZE = NL.GETLENGTH();
    FOR(INT I = 0;I < SIZE;I++)
    PROCESSNODE(SUBTNODE,NL.ITEM(I));
    }
    PROTECTED VOID PROCESSENTITY(DEFAULTMUTABLETREENODE TNODE,ENTITY ENT)
    {
    // DOES NOTHING AS WE DON'T BOTHER WITH DTD
    }
    PROTECTED VOID PROCESSENTITYREFERENCE(DEFAULTMUTABLETREENODE TNODE,ENTITYREFERENCE ER)
    {
    DEFAULTMUTABLETREENODE SUBTNODE =
    NEW DEFAULTMUTABLETREENODE("&" + ER.GETNODENAME() + ";");
    TNODE.ADD(SUBTNODE);
    }
    PROTECTED VOID PROCESSNOTATION(DEFAULTMUTABLETREENODE TNODE,NOTATION NOT)
    {
    // DOES NOTHING AS WE DON'T BOTHER WITH DTD
    }
    PROTECTED VOID PROCESSPROCESSINGINSTRUCTION(DEFAULTMUTABLETREENODE TNODE,PROCESSINGINSTRUCTION PI)
    {
    DEFAULTMUTABLETREENODE SUBTNODE =
    NEW DEFAULTMUTABLETREENODE("<?" + PI.GETTARGET() + ' ' + PI.GETDATA() + "?>");
    TNODE.ADD(SUBTNODE);
    }
    PROTECTED VOID PROCESSTEXT(DEFAULTMUTABLETREENODE TNODE,TEXT TEXT)
    {
    STRING ST = TEXT.GETDATA();
    // NO NEED TO DISPLAY THE NEWLINES BETWEEN ELEMENTS!
    IF(ST.TRIM().LENGTH() != 0)
    {
    DEFAULTMUTABLETREENODE SUBTNODE = NEW DEFAULTMUTABLETREENODE(ST);
    TNODE.ADD(SUBTNODE);
    }
    }
    STATIC PUBLIC VOID MAIN(STRING[] ARGS)
    THROWS UNSUPPORTEDLOOKANDFEELEXCEPTION, ILLEGALACCESSEXCEPTION,
    INSTANTIATIONEXCEPTION, CLASSNOTFOUNDEXCEPTION
    {
    UIMANAGER.SETLOOKANDFEEL(UIMANAGER.GETSYSTEMLOOKANDFEELCLASSNAME());
    NEW VIEWXML();
    }
    }