This commit was manufactured by cvs2svn to create tag 'dev1-0-0'.

X-SVN-Rev: 943
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..62b3798
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+* text=auto !eol
+
+*.c text !eol
+*.cc text !eol
+*.classpath text !eol
+*.cpp text !eol
+*.css text !eol
+*.dsp text !eol
+*.dsw text !eol
+*.filters text !eol
+*.h text !eol
+*.htm text !eol
+*.html text !eol
+*.in text !eol
+*.java text !eol
+*.launch text !eol
+*.mak text !eol
+*.md text !eol
+*.MF text !eol
+*.mk text !eol
+*.pl text !eol
+*.pm text !eol
+*.project text !eol
+*.properties text !eol
+*.py text !eol
+*.rc text !eol
+*.sh text eol=lf
+*.sln text !eol
+*.stub text !eol
+*.txt text !eol
+*.ucm text !eol
+*.vcproj text !eol
+*.vcxproj text !eol
+*.xml text !eol
+*.xsl text !eol
+*.xslt text !eol
+Makefile text !eol
+configure text !eol
+LICENSE text !eol
+README text !eol
+
+*.bin -text
+*.brk -text
+*.cnv -text
+*.icu -text
+*.res -text
+*.nrm -text
+*.spp -text
+*.tri2 -text
+
+src/com/ibm/demo/rbbi/english.dict -text
+src/com/ibm/test/rbbi/english.dict -text
+src/com/ibm/text/resources/thai_dict -text
+src/data/holidays_jp.ucs -text
+
+# The following file types are stored in Git-LFS.
+*.jar filter=lfs diff=lfs merge=lfs -text
+*.dat filter=lfs diff=lfs merge=lfs -text
+*.zip filter=lfs diff=lfs merge=lfs -text
+*.gz filter=lfs diff=lfs merge=lfs -text
+*.bz2 filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+
diff --git a/build.bat b/build.bat
new file mode 100755
index 0000000..6fd4be8
--- /dev/null
+++ b/build.bat
@@ -0,0 +1,15 @@
+@echo off
+
+REM *******************************************************************************
+REM * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+REM * others. All Rights Reserved.                                                *
+REM *******************************************************************************
+
+REM $Source: /xsrl/Nsvn/icu/icu4j/Attic/build.bat,v $ 
+REM $Date: 2000/03/10 23:50:23 $ 
+REM $Revision: 1.3 $
+
+REM *******************************************************************************
+
+REM convience bat file to build with
+java -classpath "build\javac.jar;build\ant.jar;build\projectx-tr2.jar;%CLASSPATH%" org.apache.tools.ant.Main %1 %2 %3 %4 %5
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..ee6ba6d
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,26 @@
+#/*
+#*******************************************************************************
+#* Copyright (C) 1997-2000, International Business Machines Corporation and    *
+#* others. All Rights Reserved.                                                *
+#*******************************************************************************
+#*
+#* $Source: /xsrl/Nsvn/icu/icu4j/Attic/build.sh,v $ 
+#* $Date: 2000/03/10 04:17:55 $ 
+#* $Revision: 1.2 $
+#*
+#*****************************************************************************************
+#*/
+#!/bin/sh
+
+ADDL_CLASSPATH=build/ant.jar:build/projectx-tr2.jar:build/javac.jar
+
+if [ "$CLASSPATH" != "" ] ; then
+  CLASSPATH=$CLASSPATH:$ADDL_CLASSPATH
+else
+ CLASSPATH=$ADDL_CLASSPATH
+fi
+export CLASSPATH
+
+echo Building with classpath $CLASSPATH
+
+java org.apache.tools.ant.Main $*
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..d18db7c
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,90 @@
+<!-- 
+/*
+*******************************************************************************
+* Copyright (C) 1997-2000, International Business Machines Corporation and    *
+* others. All Rights Reserved.                                                *
+*******************************************************************************
+*
+* $Source: /xsrl/Nsvn/icu/icu4j/build.xml,v $ 
+* $Date: 2000/03/10 04:17:55 $ 
+* $Revision: 1.2 $
+*
+*****************************************************************************************
+*/
+-->
+
+<project name="ICU4J" default="core" basedir=".">
+
+  <property name="src.dir" value="src"/>
+  <property name="doc.dir" value="doc"/>
+  <property name="build.dir" value="classes"/>
+  <property name="classpath" value="${build.dir}"/>
+  <property name="build.compiler" value="classic"/>
+  
+  <target name="all" depends="core,tests,tools,demos,jar,docs"/>
+  
+  <target name="core">
+    <mkdir dir="${build.dir}"/>
+    <copyfile src="${src.dir}/com/ibm/text/resources/thai_dict"
+    	   dest="${build.dir}/com/ibm/text/resources/thai_dict"/>
+    <compile srcfiles="com/ibm/util,com/ibm/text"
+    	   srcdir="${src.dir}"
+    	   destdir="${build.dir}" 
+    	   classpath="${classpath}"
+           debug="on" deprecation="off" target="1.2"/>
+  </target>
+  
+  <target name="tests" depends="core">
+    <mkdir dir="${build.dir}"/>
+    <compile srcfiles="com/ibm/test"
+    	   srcdir="${src.dir}"
+    	   destdir="${build.dir}" 
+    	   classpath="${classpath}"
+           debug="on" deprecation="off" target="1.2"/>
+  </target>
+  
+  <target name="demos" depends="core">
+    <mkdir dir="${build.dir}"/>
+    <compile srcfiles="com/ibm/demo"
+    	   srcdir="${src.dir}"
+    	   destdir="${build.dir}" 
+    	   classpath="${classpath}"
+           debug="on" deprecation="off" target="1.2"/>
+  </target>
+  
+  <target name="tools" depends="core">
+    <mkdir dir="${build.dir}"/>
+    <compile srcfiles="com/ibm/tools"
+    	   srcdir="${src.dir}"
+    	   destdir="${build.dir}" 
+    	   classpath="${classpath}"
+           debug="on" deprecation="off" target="1.2"/>
+  </target>
+  
+  <target name="docs" depends="core,tests,tools">
+    <mkdir dir="${doc.dir}"/>
+    <docs sourcepath="${src.dir}"
+    		  destdir="${doc.dir}"
+    		  nodeprecatedlist="true"
+			  windowtitle="ICU4J"
+			  doctitle="ICU4J"
+			  encoding="iso-8859-1"
+			  docencoding="iso-8859-1"
+			  bottom="&quot;&lt;font size=-1>Copyright (c); 1998-2000 IBM Corporation.&lt;/font>&quot;"
+	    	  sourcefiles="src/com/ibm/text/*.java src/com/ibm/util/*.java"
+    		  />
+    		  
+  </target>
+
+  <target name="jar" depends="core">
+     <jar jarfile="ICU4J.jar" basedir="${build.dir}" items="com"/>
+  </target>
+
+  <target name="clean">
+    <deltree dir="${build.dir}"/>
+    <deltree dir="${doc.dir}"/>
+    <deltree dir="ICU4J.jar"/>
+  </target>
+
+</project>
+
diff --git a/license.html b/license.html
new file mode 100644
index 0000000..ede34e0
--- /dev/null
+++ b/license.html
@@ -0,0 +1,242 @@
+<html>
+
+<head>
+<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=Latin-1">
+<title>IBM's Public License - IBM's Components for Unicode in Java</title>
+</head>
+
+<body>
+<b>
+
+<p ALIGN="CENTER"><big>IBM PUBLIC LICENSE - IBM&#146;s Components for Unicode in Java VERSION 1.0</big></p>
+</b><font size="2">
+
+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS IBM PUBLIC LICENSE
+(&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
+RECIPIENT&#146;S ACCEPTANCE OF THIS AGREEMENT.</p>
+<b>
+
+<p>1. DEFINITIONS</p>
+</b>
+
+<p>&quot;Contribution&quot; means: </p>
+
+<blockquote>
+  <blockquote>
+    <p>a) in the case of International Business Machines Corporation (&quot;IBM&quot;), the
+    Original Program, and </p>
+    <p>b) in the case of each Contributor, </p>
+    <blockquote>
+      <p>i) changes to the Program, and</p>
+      <p>ii) additions to the Program;</p>
+    </blockquote>
+  </blockquote>
+  <p>where such changes and/or additions to the Program originate from and are distributed
+  by that particular Contributor. A Contribution &#145;originates&#146; from a Contributor
+  if it was added to the Program by such Contributor itself or anyone acting on such
+  Contributor&#146;s behalf. Contributions do not include additions to the Program which:
+  (i) are separate modules of software distributed in conjunction with the Program under
+  their own license agreement, and (ii) are not derivative works of the Program.</p>
+</blockquote>
+
+<p>&quot;Contributor&quot; means IBM and any other entity that distributes the Program.</p>
+
+<p>&quot;Licensed Patents &quot; mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when combined with
+the Program. </p>
+
+<p>&quot;Original Program&quot; means the original version of the software accompanying
+this Agreement as released by IBM, including source code, object code and documentation,
+if any.</p>
+
+<p>&quot;Program&quot; means the Original Program and Contributions.</p>
+
+<p>&quot;Recipient&quot; means anyone who receives the Program under this Agreement,
+including all Contributors.</p>
+<b>
+
+<p>2. GRANT OF RIGHTS</p>
+
+<blockquote>
+  <blockquote>
+    </b><p>a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient
+    a non-exclusive, worldwide, royalty-free copyright license to<font COLOR="#ff0000"> </font>reproduce,
+    prepare derivative works of, publicly display, publicly perform, distribute and sublicense
+    the Contribution of such Contributor, if any, and such derivative works, in source code
+    and object code form.</p>
+    <p>b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a
+    non-exclusive, worldwide,<font COLOR="#008000"> </font>royalty-free patent license under
+    Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the
+    Contribution of such Contributor, if any, in source code and object code form. This patent
+    license shall apply to the combination of the Contribution and the Program if, at the time
+    the Contribution is added by the Contributor, such addition of the Contribution causes
+    such combination to be covered by the Licensed Patents. The patent license shall not apply
+    to any other combinations which include the Contribution. No hardware per se is licensed
+    hereunder. </p>
+    <p>c) Recipient understands that although each Contributor grants the licenses to its
+    Contributions set forth herein, no assurances are provided by any Contributor that the
+    Program does not infringe the patent or other intellectual property rights of any other
+    entity. Each Contributor disclaims any liability to Recipient for claims brought by any
+    other entity based on infringement of intellectual property rights or otherwise. As a
+    condition to exercising the rights and licenses granted hereunder, each Recipient hereby
+    assumes sole responsibility to secure any other intellectual property rights needed, if
+    any. For example, if a third party patent license is required to allow Recipient to
+    distribute the Program, it is Recipient&#146;s responsibility to acquire that license
+    before distributing the Program.</p>
+    <p>d) Each Contributor represents that to its knowledge it has sufficient copyright rights
+    in its Contribution, if any, to grant the copyright license set forth in this Agreement. </p>
+  </blockquote>
+</blockquote>
+<b>
+
+<p>3. REQUIREMENTS</p>
+</b>
+
+<p>A Contributor may choose to distribute the Program in object code form under its own
+license agreement, provided that:</p>
+
+<blockquote>
+  <blockquote>
+    <p>a) it complies with the terms and conditions of this Agreement; and</p>
+    <p>b) its license agreement:</p>
+    <blockquote>
+      <p>i) effectively disclaims on behalf of all Contributors all warranties and conditions,
+      express and implied, including warranties or conditions of title and non-infringement, and
+      implied warranties or conditions of merchantability and fitness for a particular purpose; </p>
+      <p>ii) effectively excludes on behalf of all Contributors all liability for damages,
+      including direct, indirect, special, incidental and consequential damages, such as lost
+      profits; </p>
+      <p>iii) states that any provisions which differ from this Agreement are offered by that
+      Contributor alone and not by any other party; and</p>
+      <p>iv) states that source code for the Program is available from such Contributor, and
+      informs licensees how to obtain it in a reasonable manner on or through a medium
+      customarily used for software exchange.<font COLOR="#0000ff"> </p>
+      </font>
+    </blockquote>
+  </blockquote>
+</blockquote>
+
+<p>When the Program is made available in source code form:</p>
+
+<blockquote>
+  <blockquote>
+    <p>a) it must be made available under this Agreement; and </p>
+    <p>b) a copy of this Agreement must be included with each copy of the Program. </p>
+    <font COLOR="#0000ff"><strike>
+  </blockquote>
+</blockquote>
+</strike></font>
+
+<p>Each Contributor must include the following in a conspicuous location in the Program: </p>
+
+<blockquote>
+  <p>Copyright <font FACE="Times New Roman">©</font><font COLOR="#ff0000"> </font>1999,
+  International Business Machines Corporation and others. All Rights Reserved. </p>
+</blockquote>
+
+<p>In addition, each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify
+the originator of the Contribution. </p>
+<b>
+
+<p>4. COMMERCIAL DISTRIBUTION</p>
+</b>
+
+<p>Commercial distributors of software may accept certain responsibilities with respect to
+end users, business partners and the like. While this license is intended to facilitate
+the commercial use of the Program, the Contributor who includes the Program in a
+commercial product offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes the Program in a
+commercial product offering, such Contributor (&quot;Commercial Contributor&quot;) hereby
+agrees to defend and indemnify every other Contributor (&quot;Indemnified
+Contributor&quot;) against any losses, damages and costs (collectively &quot;Losses&quot;)
+arising from claims, lawsuits and other legal actions brought by a third party against the
+Indemnified Contributor to the extent caused by the acts or omissions of such Commercial
+Contributor in connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses relating to
+any actual or alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of
+such claim, and b) allow the Commercial Contributor to control, and cooperate with the
+Commercial Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own expense.</p>
+
+<p>For example, a Contributor might include the Program in a commercial product offering,
+Product X. That Contributor is then a Commercial Contributor. If that Commercial
+Contributor then makes performance claims, or offers warranties related to Product X,
+those performance claims and warranties are such Commercial Contributor&#146;s
+responsibility alone. Under this section, the Commercial Contributor would have to defend
+claims against the other Contributors related to those performance claims and warranties,
+and if a court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.</p>
+<b>
+
+<p>5. NO WARRANTY</p>
+</b>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN &quot;AS
+IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED
+INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible
+for determining the appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement, including but not
+limited to the risks and costs of program errors, compliance with applicable laws, damage
+to or loss of data, programs or equipment, and unavailability or interruption of
+operations. </p>
+<b>
+
+<p>6. DISCLAIMER OF LIABILITY</p>
+</b>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM
+OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.</p>
+<b>
+
+<p>7. GENERAL</p>
+</b>
+
+<p>If any provision of this Agreement is invalid or unenforceable under applicable law, it
+shall not affect the validity or enforceability of the remainder of the terms of this
+Agreement, and without further action by the parties hereto, such provision shall be
+reformed to the minimum extent necessary to make such provision valid and enforceable.</p>
+
+<p>If Recipient institutes patent litigation against a Contributor with respect to a
+patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then
+any patent licenses granted by that Contributor to such Recipient under this Agreement
+shall terminate as of the date such litigation is filed. In addition, If Recipient
+institutes patent litigation against any entity (including a cross-claim or counterclaim
+in a lawsuit) alleging that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient&#146;s patent(s), then such
+Recipient&#146;s rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed. </p>
+
+<p>All Recipient&#146;s rights under this Agreement shall terminate if it fails to comply
+with any of the material terms or conditions of this Agreement and does not cure such
+failure in a reasonable period of time after becoming aware of such noncompliance. If all
+Recipient&#146;s rights under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient relating to the
+Program shall continue and survive. </p>
+
+<p>IBM may publish new versions (including revisions) of this Agreement from time to time.
+Each new version of the Agreement will be given a distinguishing version number. The
+Program (including Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version of the Agreement
+is published, Contributor may elect to distribute the Program (including its
+Contributions) under the new version. No one other than IBM has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives
+no rights or licenses to the intellectual property of any Contributor under this
+Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the
+Program not expressly granted under this Agreement are reserved.</p>
+
+<p>This Agreement is governed by the laws of the State of New York and the intellectual
+property laws of the United States of America. No party to this Agreement will bring a
+legal action under this Agreement more than one year after the cause of action arose. Each
+party waives its rights to a jury trial in any resulting litigation. </p>
+</font>
+</body>
+</html>
diff --git a/src/com/ibm/demo/DemoApplet.java b/src/com/ibm/demo/DemoApplet.java
new file mode 100755
index 0000000..e3ed204
--- /dev/null
+++ b/src/com/ibm/demo/DemoApplet.java
@@ -0,0 +1,80 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/Attic/DemoApplet.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo;
+
+import java.applet.Applet;
+import java.util.Locale;
+import java.awt.*;
+import java.awt.event.*;
+
+public abstract class DemoApplet extends java.applet.Applet {
+    private Button   demoButton;
+    private Frame    demoFrame;
+	private static int demoFrameCount = 0;
+
+    protected abstract Frame createDemoFrame(DemoApplet applet);
+    protected Dimension getDefaultFrameSize(DemoApplet applet, Frame f) {
+    	return new Dimension(700, 550);
+    }
+
+    //Create a button that will display the demo
+    public void init()
+    {
+        setBackground(Color.white);
+        demoButton = new Button("Demo");
+        demoButton.setBackground(Color.yellow);
+        add( demoButton );
+
+        demoButton.addActionListener( new ActionListener() {
+             public void actionPerformed(ActionEvent e) {
+                if (e.getID() == ActionEvent.ACTION_PERFORMED) {
+                    demoButton.setLabel("loading");
+
+                    if (demoFrame == null) {
+                       demoFrame = createDemoFrame(DemoApplet.this);
+                       showDemo();
+                    }
+
+                    demoButton.setLabel("Demo");
+                }
+             }
+        } );
+    }
+
+    public void showDemo()
+    {
+    	demoFrame = createDemoFrame(this);
+        demoFrame.layout();
+        Dimension d = getDefaultFrameSize(this, demoFrame);
+        demoFrame.resize(d.width, d.height);
+        demoFrame.show();
+		demoFrameOpened();
+    }
+
+    public void demoClosed()
+    {
+        demoFrame = null;
+		demoFrameClosed();
+    }
+
+	protected static void demoFrameOpened() {
+		demoFrameCount++;
+    }
+	protected static void demoFrameClosed() {
+		if (--demoFrameCount == 0) {
+			System.exit(0);
+		}
+    }
+}
+
diff --git a/src/com/ibm/demo/DemoTextBox.java b/src/com/ibm/demo/DemoTextBox.java
new file mode 100755
index 0000000..3d11497
--- /dev/null
+++ b/src/com/ibm/demo/DemoTextBox.java
@@ -0,0 +1,101 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/Attic/DemoTextBox.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo;
+
+
+import java.text.BreakIterator;
+import java.awt.*;
+
+public class DemoTextBox {
+
+    public DemoTextBox(Graphics g, String text, int width)
+    {
+        this.text = text;
+        this.chars = new char[text.length()];
+        text.getChars(0, text.length(), chars, 0);
+
+        this.width = width;
+        this.port = g;
+        this.metrics = g.getFontMetrics();
+
+        breakText();
+    }
+
+    public  int getHeight() {
+        return (nbreaks + 1) * metrics.getHeight();
+    }
+
+    public  void draw(Graphics g, int x, int y)
+    {
+        int index = 0;
+
+        y += metrics.getAscent();
+
+        for (int i = 0; i < nbreaks; i++)
+        {
+            g.drawChars(chars, index, breakPos[i] - index, x, y);
+            index = breakPos[i];
+            y += metrics.getHeight();
+        }
+
+        g.drawChars(chars, index, chars.length - index, x, y);
+    }
+
+
+    private void breakText()
+    {
+        if (metrics.charsWidth(chars, 0, chars.length) > width)
+        {
+            BreakIterator iter = BreakIterator.getWordInstance();
+            iter.setText(text);
+
+            int start = iter.first();
+            int end = start;
+            int pos;
+
+            while ( (pos = iter.next()) != BreakIterator.DONE )
+            {
+                int w = metrics.charsWidth(chars, start, pos - start);
+                if (w > width)
+                {
+                    // We've gone past the maximum width, so break the line
+                    if (end > start) {
+                        // There was at least one break position before this point
+                        breakPos[nbreaks++] = end;
+                        start = end;
+                        end = pos;
+                    } else {
+                        // There weren't any break positions before this one, so
+                        // let this word overflow the margin (yuck)
+                        breakPos[nbreaks++] = pos;
+                        start = end = pos;
+                    }
+                } else {
+                    // the current position still fits on the line; it's the best
+                    // tentative break position we have so far.
+                    end = pos;
+                }
+
+            }
+        }
+    }
+
+    private String          text;
+    private char[]          chars;
+    private Graphics        port;
+    private FontMetrics     metrics;
+    private int             width;
+
+    private int[]           breakPos = new int[10]; // TODO: get real
+    private int             nbreaks = 0;
+}
\ No newline at end of file
diff --git a/src/com/ibm/demo/DemoUtility.java b/src/com/ibm/demo/DemoUtility.java
new file mode 100755
index 0000000..ed57b16
--- /dev/null
+++ b/src/com/ibm/demo/DemoUtility.java
@@ -0,0 +1,110 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/Attic/DemoUtility.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo;
+
+import java.awt.*;
+import java.lang.*;
+import java.util.*;
+
+public class DemoUtility
+{
+    public static final Font titleFont = new Font("TimesRoman",Font.BOLD,18);
+    public static final Font labelFont = new Font("TimesRoman",Font.BOLD,14);
+    public static final Font choiceFont = new Font("Helvetica",Font.BOLD,12);
+    public static final Font editFont = new Font("Helvetica",Font.PLAIN,14);
+    public static final Font creditFont = new Font("Helvetica",Font.PLAIN,10);
+    public static final Font numberFont = new Font("sansserif", Font.PLAIN, 14);
+
+    public static final Color bgColor = Color.lightGray;
+    public static final Color choiceColor = Color.white;
+
+    public static final String copyright1 =
+        "(C) Copyright Taligent, Inc. 1996-1998.  Copyright (C) IBM, Inc. 1998 - All Rights Reserved";
+    public static final String copyright2 =
+        "Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.";
+
+    /**
+    Provides easy way to use basic functions of GridBagLayout, without
+    the complications. After building a panel, and inserting all the
+    * subcomponents, call this to lay it out in the desired number of columns.
+    */
+    public static void fixGrid(Container cont, int columns) {
+        GridBagLayout gridbag = new GridBagLayout();
+        cont.setLayout(gridbag);
+
+        GridBagConstraints c = new GridBagConstraints();
+        c.fill = GridBagConstraints.VERTICAL;
+        c.weightx = 1.0;
+        c.insets = new Insets(2,2,2,2);
+
+        Component[] components = cont.getComponents();
+        for (int i = 0; i < components.length; ++i) {
+            int colNumber = i%columns;
+            c.gridwidth = 1;    // default
+            if ((i%columns) == columns - 1)
+                c.gridwidth = GridBagConstraints.REMAINDER;    // last in grid
+            if (components[i] instanceof Label) {
+                switch (((Label)components[i]).getAlignment()) {
+                case Label.CENTER: c.anchor = GridBagConstraints.CENTER; break;
+                case Label.LEFT: c.anchor = GridBagConstraints.WEST; break;
+                case Label.RIGHT: c.anchor = GridBagConstraints.EAST; break;
+                }
+            }
+            gridbag.setConstraints(components[i], c);
+        }
+
+    }
+
+    /**
+    Provides easy way to change the spacing around an object in a GridBagLayout.
+    Call AFTER fixGridBag, passing in the container, the component, and the
+    new insets.
+    */
+    public static void setInsets(Container cont, Component comp, Insets insets) {
+        GridBagLayout gbl = (GridBagLayout)cont.getLayout();
+        GridBagConstraints g = gbl.getConstraints(comp);
+        g.insets = insets;
+        gbl.setConstraints(comp,g);
+    }
+
+    public static Panel createSpacer() {
+        Panel spacer = new Panel();
+        spacer.setLayout(null);
+        spacer.resize(1000, 1);
+        return spacer;
+    }
+
+    // to avoid goofy updates and misplaced cursors
+    public static void setText(TextComponent area, String newText) {
+        String foo = area.getText();
+        if (foo.equals(newText)) return;
+        area.setText(newText);
+    }
+    /**
+     * Get the G7 locale list for demos.
+     */
+    public static Locale[] getG7Locales() {
+        return localeList;
+    }
+    private static Locale[] localeList = {
+        new Locale("DA", "DK", ""),
+        new Locale("EN", "US", ""),
+        new Locale("EN", "GB", ""),
+        new Locale("EN", "CA", ""),
+        new Locale("FR", "FR", ""),
+        new Locale("FR", "CA", ""),
+        new Locale("DE", "DE", ""),
+        new Locale("IT", "IT", ""),
+    //new Locale("JA", "JP", ""),
+    };
+}
diff --git a/src/com/ibm/demo/calendar/CalendarApp.java b/src/com/ibm/demo/calendar/CalendarApp.java
new file mode 100755
index 0000000..811eaa3
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarApp.java
@@ -0,0 +1,48 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/calendar/Attic/CalendarApp.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.calendar;
+
+import com.ibm.demo.*;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.io.*;
+
+import java.text.DateFormat;
+import java.util.SimpleTimeZone;
+
+import com.ibm.util.*;
+import com.ibm.text.*;
+
+/**
+ * CalendarApp demonstrates how Calendar works.
+ */
+public class CalendarApp extends DemoApplet
+{
+    /**
+     * The main function which defines the behavior of the CalendarDemo
+     * applet when an applet is started.
+     */
+    public static void main(String argv[]) {
+
+        new CalendarApp().showDemo();
+    }
+
+    /* This creates a CalendarFrame for the demo applet. */
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new CalendarFrame(applet);
+    }
+}
diff --git a/src/com/ibm/demo/calendar/CalendarCalc.java b/src/com/ibm/demo/calendar/CalendarCalc.java
new file mode 100755
index 0000000..23e51bb
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarCalc.java
@@ -0,0 +1,559 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/calendar/Attic/CalendarCalc.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.calendar;
+
+import com.ibm.demo.*;
+
+import java.applet.Applet;
+import java.util.Date;
+import java.awt.*;
+import java.awt.event.*;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.util.Locale;
+
+import com.ibm.util.*;
+import com.ibm.text.*;
+
+import javax.swing.*;
+
+/**
+ * CalendarCalc demonstrates how Date/Time formatter works.
+ */
+public class CalendarCalc extends DemoApplet
+{
+    /**
+     * The main function which defines the behavior of the MultiCalendarDemo
+     * applet when an applet is started.
+     */
+    public static void main(String argv[]) {
+        new CalendarCalc().showDemo();
+    }
+
+    /**
+     * This creates a CalendarCalcFrame for the demo applet.
+     */
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new CalendarCalcFrame(applet);
+    }
+}
+
+/**
+ * A Frame is a top-level window with a title. The default layout for a frame
+ * is BorderLayout.  The CalendarCalcFrame class defines the window layout of
+ * MultiCalendarDemo.
+ */
+class CalendarCalcFrame extends Frame
+{
+    private static final String     creditString = "";
+
+    static final Locale[] locales = DemoUtility.getG7Locales();
+
+    private static final boolean    DEBUG = false;
+
+    private DemoApplet              applet;
+    private long                    time = System.currentTimeMillis();
+
+    private static final RollAddField kRollAddFields[] = {
+        new RollAddField(Calendar.YEAR,                 "Year" ),
+        new RollAddField(Calendar.MONTH,                "Month" ),
+        new RollAddField(Calendar.WEEK_OF_MONTH,        "Week of Month" ),
+        new RollAddField(Calendar.WEEK_OF_YEAR,         "Week of Year" ),
+        new RollAddField(Calendar.DAY_OF_MONTH,         "Day of Month" ),
+        new RollAddField(Calendar.DAY_OF_WEEK,          "Day of Week" ),
+        new RollAddField(Calendar.DAY_OF_WEEK_IN_MONTH, "Day of Week in Month" ),
+        new RollAddField(Calendar.DAY_OF_YEAR,          "Day of Year" ),
+        new RollAddField(Calendar.AM_PM,                "AM/PM" ),
+        new RollAddField(Calendar.HOUR_OF_DAY,          "Hour of day" ),
+        new RollAddField(Calendar.HOUR,                 "Hour" ),
+        new RollAddField(Calendar.MINUTE,               "Minute" ),
+        new RollAddField(Calendar.SECOND,               "Second" ),
+    };
+
+    /**
+     * Constructs a new CalendarCalcFrame that is initially invisible.
+     */
+    public CalendarCalcFrame(DemoApplet applet)
+    {
+        super("Multiple Calendar Demo");
+        this.applet = applet;
+        init();
+        start();
+    }
+
+    /**
+     * Initializes the applet. You never need to call this directly, it
+     * is called automatically by the system once the applet is created.
+     */
+    public void init()
+    {
+        buildGUI();
+
+        patternText.setText( calendars[0].toPattern() );
+
+        // Force an update of the display
+        cityChanged();
+        millisFormat();
+    }
+
+    //------------------------------------------------------------
+    // package private
+    //------------------------------------------------------------
+    void addWithFont(Container container, Component foo, Font font) {
+        if (font != null)
+            foo.setFont(font);
+        container.add(foo);
+    }
+
+    /**
+     * Called to start the applet. You never need to call this method
+     * directly, it is called when the applet's document is visited.
+     */
+    public void start()
+    {
+        // do nothing
+    }
+
+    TextField patternText;
+
+    Choice dateMenu;
+    Choice localeMenu;
+
+    Button up;
+    Button down;
+
+    Checkbox getRoll;
+    Checkbox getAdd;
+
+    public void buildGUI()
+    {
+        setBackground(DemoUtility.bgColor);
+        setLayout(new FlowLayout()); // shouldn't be necessary, but it is.
+
+// TITLE
+        Label label1=new Label("Calendar Converter", Label.CENTER);
+        label1.setFont(DemoUtility.titleFont);
+        add(label1);
+        add(DemoUtility.createSpacer());
+
+// IO Panel
+        Panel topPanel = new Panel();
+        topPanel.setLayout(new FlowLayout());
+
+        CheckboxGroup group1= new CheckboxGroup();
+
+        // Set up the controls for each calendar we're demonstrating
+        for (int i = 0; i < calendars.length; i++)
+        {
+            Label label = new Label(calendars[i].name, Label.RIGHT);
+            label.setFont(DemoUtility.labelFont);
+            topPanel.add(label);
+
+            topPanel.add(calendars[i].text);
+
+            final int j = i;
+            calendars[i].text.addActionListener( new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    textChanged(j);
+                }
+            } );
+
+            calendars[i].rollAdd.setCheckboxGroup(group1);
+            topPanel.add(calendars[i].rollAdd);
+        }
+        calendars[0].rollAdd.setState(true);    // Make the first one selected
+
+        Label label4=new Label("Pattern", Label.RIGHT);
+        label4.setFont(DemoUtility.labelFont);
+        topPanel.add(label4);
+
+        patternText=new TextField(FIELD_COLUMNS);
+        patternText.setFont(DemoUtility.editFont);
+        topPanel.add(patternText);
+        topPanel.add(new Label(""));
+
+        DemoUtility.fixGrid(topPanel,3);
+        add(topPanel);
+        add(DemoUtility.createSpacer());
+
+// ROLL / ADD
+        Panel rollAddPanel=new Panel();
+        {
+            rollAddPanel.setLayout(new FlowLayout());
+
+            Panel rollAddBoxes = new Panel();
+            {
+                rollAddBoxes.setLayout(new GridLayout(2,1));
+                CheckboxGroup group2= new CheckboxGroup();
+                getRoll = new Checkbox("Roll",group2, false);
+                getAdd = new Checkbox("Add",group2, true);
+
+                rollAddBoxes.add(getRoll);
+                rollAddBoxes.add(getAdd);
+            }
+
+            Label dateLabel=new Label("Date Fields");
+            dateLabel.setFont(DemoUtility.labelFont);
+
+            dateMenu= new Choice();
+            dateMenu.setBackground(DemoUtility.choiceColor);
+            for (int i = 0; i < kRollAddFields.length; i++) {
+                dateMenu.addItem(kRollAddFields[i].name);
+                if (kRollAddFields[i].field == Calendar.MONTH) {
+                    dateMenu.select(i);
+                }
+            }
+
+            Panel upDown = new Panel();
+            {
+                upDown.setLayout(new GridLayout(2,1));
+
+                // *** If the images are not found, we use the label.
+                up = new Button("^");
+                down = new Button("v");
+                up.setBackground(DemoUtility.bgColor);
+                down.setBackground(DemoUtility.bgColor);
+                upDown.add(up);
+                upDown.add(down);
+            }
+
+            rollAddPanel.add(dateLabel);
+            rollAddPanel.add(dateMenu);
+            rollAddPanel.add(rollAddBoxes);
+            rollAddPanel.add(upDown);
+
+        }
+        Panel localePanel = new Panel();
+        {
+            // Make the locale popup menus
+            localeMenu= new Choice();
+
+            int selectMe = 0;
+            for (int i = 0; i < locales.length; i++) {
+                if (i > 0 && locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
+                    i < locales.length - 1 &&
+                        locales[i].getLanguage().equals(locales[i+1].getLanguage()))
+                {
+                    localeMenu.addItem( locales[i].getDisplayName() );
+                } else {
+                    localeMenu.addItem( locales[i].getDisplayLanguage());
+                }
+                if (locales[i].getLanguage().equals(Locale.getDefault().getLanguage())) {
+                    selectMe = i;
+                }
+            }
+            localeMenu.setBackground(DemoUtility.choiceColor);
+            localeMenu.select(selectMe);
+
+            Label localeLabel =new Label("Display Locale");
+            localeLabel.setFont(DemoUtility.labelFont);
+
+            localePanel.add(localeLabel);
+            localePanel.add(localeMenu);
+            DemoUtility.fixGrid(localePanel,2);
+
+            localeMenu.addItemListener( new ItemListener() {
+                public void itemStateChanged(ItemEvent e) {
+                    Locale loc = locales[localeMenu.getSelectedIndex()];
+                    System.out.println("Change locale to " + loc.getDisplayName());
+
+                    for (int i = 0; i < calendars.length; i++) {
+                        calendars[i].setLocale(loc);
+                    }
+                    millisFormat();
+                }
+            } );
+        }
+        add(rollAddPanel);
+        add(DemoUtility.createSpacer());
+        add(localePanel);
+        add(DemoUtility.createSpacer());
+
+// COPYRIGHT
+        Panel copyrightPanel = new Panel();
+        addWithFont (copyrightPanel,new Label(DemoUtility.copyright1, Label.LEFT),
+            DemoUtility.creditFont);
+        DemoUtility.fixGrid(copyrightPanel,1);
+        add(copyrightPanel);
+    }
+
+    /**
+     * Called if an action occurs in the CalendarCalcFrame object.
+     */
+    public boolean action(Event evt, Object obj)
+    {
+        // *** Button events are handled here.
+        if (evt.target instanceof Button) {
+            if (evt.target == up) {
+                    dateFieldChanged(false);
+                    return true;
+            } else
+            if (evt.target == down) {
+                    dateFieldChanged(true);
+                    return true;
+            }
+        }
+        return super.action(evt, obj);
+    }
+
+    /**
+     * Handles the event. Returns true if the event is handled and should not
+     * be passed to the parent of this component. The default event handler
+     * calls some helper methods to make life easier on the programmer.
+     */
+    public boolean handleEvent(Event evt)
+    {
+        if (evt.id == Event.KEY_RELEASE && evt.target == patternText) {
+            patternTextChanged();
+            return true;
+        }
+        else if (evt.id == Event.KEY_RELEASE) {
+            for (int i = 0; i < calendars.length; i++) {
+                if (evt.target == calendars[i].text) {
+                    textChanged(i);
+                    return true;
+                }
+            }
+        }
+        else if (evt.id == Event.ACTION_EVENT && evt.target == up) {
+            dateFieldChanged(true);
+            return true;
+        }
+        else if (evt.id == Event.ACTION_EVENT && evt.target == down) {
+            dateFieldChanged(false);
+            return true;
+        }
+        else if (evt.id == Event.WINDOW_DESTROY && evt.target == this) {
+            this.hide();
+            this.dispose();
+
+            if (applet != null) {
+               applet.demoClosed();
+            } else System.exit(0);
+
+            return true;
+        }
+
+        return super.handleEvent(evt);
+    }
+
+    /**
+     * This function is called when users change the pattern text.
+     */
+    public void setFormatFromPattern() {
+        String timePattern = patternText.getText();
+
+        for (int i = 0; i < calendars.length; i++) {
+            calendars[i].applyPattern(timePattern);
+        }
+
+        millisFormat();
+    }
+
+    /**
+     * This function is called when it is necessary to parse the time
+     * string in one of the formatted date fields
+     */
+    public void textChanged(int index) {
+        String rightString = calendars[index].text.getText();
+
+        ParsePosition status = new ParsePosition(0);
+
+        if (rightString.length() == 0)
+        {
+            errorText("Error: no input to parse!");
+            return;
+        }
+
+        try {
+            Date date = calendars[index].format.parse(rightString, status);
+            time = date.getTime();
+        }
+        catch (Exception e) {
+            for (int i = 0; i < calendars.length; i++) {
+                if (i != index) {
+                    calendars[i].text.setText("ERROR");
+                }
+            }
+            errorText("Exception: " + e.getClass().toString() + " parsing: "+rightString);
+            return;
+        }
+
+        int start = calendars[index].text.getSelectionStart();
+        int end = calendars[index].text.getSelectionEnd();
+
+        millisFormat();
+
+        calendars[index].text.select(start,end);
+    }
+
+    /**
+     * This function is called when it is necessary to format the time
+     * in the "Millis" text field.
+     */
+    public void millisFormat() {
+        String out = "";
+
+        for (int i = 0; i < calendars.length; i++) {
+            try {
+                out = calendars[i].format.format(new Date(time));
+                calendars[i].text.setText(out);
+            }
+            catch (Exception e) {
+                calendars[i].text.setText("ERROR");
+                errorText("Exception: " + e.getClass().toString() + " formatting "
+                            + calendars[i].name + " " + time);
+            }
+        }
+    }
+
+
+    /**
+     * This function is called when users change the pattern text.
+     */
+    public void patternTextChanged() {
+        setFormatFromPattern();
+    }
+
+    /**
+     * This function is called when users select a new representative city.
+     */
+    public void cityChanged() {
+        TimeZone timeZone = TimeZone.getDefault();
+
+        for (int i = 0; i < calendars.length; i++) {
+            calendars[i].format.setTimeZone(timeZone);
+        }
+        millisFormat();
+    }
+
+    /**
+     * This function is called when users select a new time field
+     * to add or roll its value.
+     */
+    public void dateFieldChanged(boolean up) {
+        int field = kRollAddFields[dateMenu.getSelectedIndex()].field;
+
+        for (int i = 0; i < calendars.length; i++)
+        {
+            if (calendars[i].rollAdd.getState())
+            {
+                Calendar c = calendars[i].calendar;
+                c.setTime(new Date(time));
+
+                if (getAdd.getState()) {
+                    c.add(field, up ? 1 : -1);
+                } else {
+                    c.roll(field, up);
+                }
+
+                time = c.getTime().getTime();
+                millisFormat();
+                break;
+            }
+        }
+    }
+
+    /**
+     * Print out the error message while debugging this program.
+     */
+    public void errorText(String s)
+    {
+        if (true) {
+            System.out.println(s);
+        }
+    }
+
+    private static final int        FIELD_COLUMNS = 35;
+    private static final String     DEFAULT_FORMAT = "EEEE MMMM d, yyyy G";
+
+
+    class CalendarRec {
+        public CalendarRec(String nameStr, java.util.Calendar cal)
+        {
+            name = nameStr;
+            calendar = cal;
+            rollAdd = new Checkbox();
+
+            text = new JTextField("",FIELD_COLUMNS);
+            text.setFont(DemoUtility.editFont);
+
+            format = IBMCalendar.getDateFormat(cal, DateFormat.FULL,
+                                                Locale.getDefault());
+            //format.applyPattern(DEFAULT_FORMAT);
+        }
+
+        public void setLocale(Locale loc) {
+            String pattern = toPattern();
+
+            format = IBMCalendar.getDateFormat(calendar, DateFormat.FULL,
+                                                                    loc);
+            applyPattern(pattern);
+        }
+
+        public void applyPattern(String pattern) {
+            if (format instanceof SimpleDateFormat) {
+                ((SimpleDateFormat)format).applyPattern(pattern);
+            } else if (format instanceof java.text.SimpleDateFormat) {
+                ((java.text.SimpleDateFormat)format).applyPattern(pattern);
+            }
+        }
+        
+        private String toPattern() {
+            if (format instanceof SimpleDateFormat) {
+                return ((SimpleDateFormat)format).toPattern();
+            } else if (format instanceof java.text.SimpleDateFormat) {
+                return ((java.text.SimpleDateFormat)format).toPattern();
+            } else {
+                return "";
+            }
+        }
+
+        java.util.Calendar  calendar;
+        DateFormat          format;
+        String              name;
+        JTextField           text;
+        Checkbox            rollAdd;
+    };
+
+    private final CalendarRec[] calendars = {
+        new CalendarRec("Gregorian",        new GregorianCalendar()),
+        new CalendarRec("Hebrew",           new HebrewCalendar()),
+        new CalendarRec("Islamic (civil)",  makeIslamic(true)),
+        new CalendarRec("Islamic (true)",   makeIslamic(false)),
+        new CalendarRec("Buddhist",         new BuddhistCalendar()),
+        new CalendarRec("Japanese",         new JapaneseCalendar()),
+//        new CalendarRec("Chinese",          new ChineseCalendar()),
+    };
+
+    static private final Calendar makeIslamic(boolean civil) {
+        IslamicCalendar cal = new IslamicCalendar();
+        cal.setCivil(civil);
+        return cal;
+    };
+};
+
+class RollAddField {
+    RollAddField(int field, String name) {
+        this.field = field;
+        this.name = name;
+    }
+    int field;
+    String name;
+};
\ No newline at end of file
diff --git a/src/com/ibm/demo/calendar/CalendarFrame.java b/src/com/ibm/demo/calendar/CalendarFrame.java
new file mode 100755
index 0000000..4612cd2
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarFrame.java
@@ -0,0 +1,422 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/calendar/Attic/CalendarFrame.java,v $ 
+ * $Date: 2000/03/10 03:47:42 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.calendar;
+
+import com.ibm.demo.*;
+import com.ibm.util.IBMCalendar;
+import com.ibm.util.HebrewCalendar;
+import com.ibm.util.BuddhistCalendar;
+import com.ibm.util.JapaneseCalendar;
+import com.ibm.util.IslamicCalendar;
+import com.ibm.text.SimpleDateFormat;
+import java.util.SimpleTimeZone;
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.io.*;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+/**
+ * A Frame is a top-level window with a title. The default layout for a frame
+ * is BorderLayout.  The CalendarFrame class defines the window layout of
+ * CalendarDemo.
+ */
+class CalendarFrame extends Frame
+{
+    private static final boolean DEBUG = false;
+
+    private DemoApplet applet;
+
+    /**
+     * Constructs a new CalendarFrame that is initially invisible.
+     */
+    public CalendarFrame(DemoApplet myApplet)
+    {
+        super("Calendar Demo");
+        this.applet = myApplet;
+        init();
+
+        // When the window is closed, we want to shut down the applet or application
+        addWindowListener(
+            new WindowAdapter() {
+                public void windowClosing(WindowEvent e) {
+                    setVisible(false);
+                    dispose();
+
+                    if (applet != null) {
+                        applet.demoClosed();
+                    } else System.exit(0);
+                }
+            } );
+    }
+
+    private Choice          displayMenu;
+    private Locale[]        locales = DemoUtility.getG7Locales();
+
+    private Calendar        calendars[]   = new Calendar[2];
+    private Choice          calMenu[]     = new Choice[2];
+    private ColoredLabel    monthLabel[]  = new ColoredLabel[2];
+    private DateFormat      monthFormat[] = new DateFormat[2];
+
+    private Button          prevYear;
+    private Button          prevMonth;
+    private Button          gotoToday;
+    private Button          nextMonth;
+    private Button          nextYear;
+    private CalendarPanel   calendarPanel;
+
+    private static void add(Container container, Component component,
+                            GridBagLayout g, GridBagConstraints c,
+                            int gridwidth, int weightx)
+    {
+        c.gridwidth = gridwidth;
+        c.weightx = weightx;
+        g.setConstraints(component, c);
+        container.add(component);
+    }
+
+    /**
+     * Initializes the applet. You never need to call this directly, it
+     * is called automatically by the system once the applet is created.
+     */
+    public void init() {
+        setBackground(DemoUtility.bgColor);
+        setLayout(new BorderLayout(10,10));
+
+        Panel topPanel = new Panel();
+        GridBagLayout g = new GridBagLayout();
+        topPanel.setLayout(g);
+        GridBagConstraints c = new GridBagConstraints();
+        c.fill = GridBagConstraints.HORIZONTAL;
+
+        // Build the two menus for selecting which calendar is displayed,
+        // plus the month/year label for each calendar
+        for (int i = 0; i < 2; i++) {
+            calMenu[i] = new Choice();
+            for (int j = 0; j < CALENDARS.length; j++) {
+                calMenu[i].addItem(CALENDARS[j].name);
+            }
+            calMenu[i].setBackground(DemoUtility.choiceColor);
+            calMenu[i].select(i);
+            calMenu[i].addItemListener(new CalMenuListener());
+
+            // Label for the current month name
+            monthLabel[i] = new ColoredLabel("", COLORS[i]);
+            monthLabel[i].setFont(DemoUtility.titleFont);
+
+            // And the default calendar to use for this slot
+            calendars[i] = CALENDARS[i].calendar;
+
+            add(topPanel, calMenu[i], g, c, 5, 0);
+            add(topPanel, monthLabel[i], g, c, GridBagConstraints.REMAINDER, 1);
+        }
+
+        // Now add the next/previous year/month buttons:
+        prevYear = new Button("<<");
+        prevYear.addActionListener(new AddAction(Calendar.YEAR, -1));
+
+        prevMonth = new Button("<");
+        prevMonth.addActionListener(new AddAction(Calendar.MONTH, -1));
+
+        gotoToday = new Button("Today");
+        gotoToday.addActionListener( new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e) {
+                calendarPanel.setDate( new Date() );
+                updateMonthName();
+            }
+        } );
+
+        nextMonth = new Button(">");
+        nextMonth.addActionListener(new AddAction(Calendar.MONTH, 1));
+
+        nextYear = new Button(">>");
+        nextYear.addActionListener(new AddAction(Calendar.YEAR, 1));
+
+        c.fill = GridBagConstraints.NONE;
+        add(topPanel, prevYear,  g, c, 1, 0);
+        add(topPanel, prevMonth, g, c, 1, 0);
+        add(topPanel, gotoToday, g, c, 1, 0);
+        add(topPanel, nextMonth, g, c, 1, 0);
+        add(topPanel, nextYear,  g, c, 1, 0);
+
+        // Now add the menu for selecting the display language
+        Panel displayPanel = new Panel();
+        {
+            displayMenu = new Choice();
+            int selectMe = 1;
+            for (int i = 0; i < locales.length; i++) {
+                if (i > 0 &&
+                        locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
+                    i < locales.length - 1 &&
+                        locales[i].getLanguage().equals(locales[i+1].getLanguage()))
+                {
+                    displayMenu.addItem( locales[i].getDisplayName() );
+                } else {
+                    displayMenu.addItem( locales[i].getDisplayLanguage());
+                }
+
+                if (locales[i].getLanguage().equals(Locale.getDefault().getLanguage())) {
+                    selectMe = i;
+                }
+            }
+            displayMenu.setBackground(DemoUtility.choiceColor);
+            displayMenu.select(selectMe);
+
+            displayMenu.addItemListener( new ItemListener()
+            {
+                 public void itemStateChanged(ItemEvent e) {
+                    Locale loc = locales[displayMenu.getSelectedIndex()];
+                    calendarPanel.setLocale( loc );
+                    monthFormat[0] = monthFormat[1] = null;
+                    updateMonthName();
+                    repaint();
+                }
+            } );
+
+            Label l1 = new Label("Display Language:", Label.RIGHT);
+            l1.setFont(DemoUtility.labelFont);
+
+            displayPanel.setLayout(new FlowLayout());
+            displayPanel.add(l1);
+            displayPanel.add(displayMenu);
+
+        }
+        c.fill = GridBagConstraints.NONE;
+        c.anchor = GridBagConstraints.EAST;
+
+        add(topPanel, displayPanel, g, c, GridBagConstraints.REMAINDER, 0);
+
+        // The title, buttons, etc. go in a panel at the top of the window
+        add("North", topPanel);
+
+        // The copyright notice goes at the bottom of the window
+        Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
+        copyright.setFont(DemoUtility.creditFont);
+        add("South", copyright);
+
+        // Now create the big calendar panel and stick it in the middle
+        calendarPanel = new CalendarPanel( locales[displayMenu.getSelectedIndex()] );
+        add("Center", calendarPanel);
+
+        for (int i = 0; i < 2; i++) {
+            calendarPanel.setCalendar(i, calendars[i]);
+            calendarPanel.setColor(i, COLORS[i]);
+        }
+
+        updateMonthName();
+    };
+
+
+    private void updateMonthName()
+    {
+            for (int i = 0; i < 2; i++) {
+                try {
+                    if (monthFormat[i] == null) {     // TODO: optimize
+                        DateFormat f = IBMCalendar.getDateTimeFormat(
+                                                calendars[i], DateFormat.MEDIUM, -1,
+                                                locales[displayMenu.getSelectedIndex()]);
+                        if (f instanceof com.ibm.text.SimpleDateFormat) {
+                            com.ibm.text.SimpleDateFormat f1 = (com.ibm.text.SimpleDateFormat) f;
+                            f1.applyPattern("MMMM, yyyy G");
+                            f1.setTimeZone(new SimpleTimeZone(0, "UTC"));
+                        } else if (f instanceof java.text.SimpleDateFormat) {
+                            java.text.SimpleDateFormat f1 = (java.text.SimpleDateFormat) f;
+                            f1.applyPattern("MMMM, yyyy G");
+                            f1.setTimeZone(new SimpleTimeZone(0, "UTC"));
+                        }
+                        monthFormat[i] = f;
+                    }
+                } catch (ClassCastException e) {
+                    //hey {lw} - there's something wrong in this routine that cuases exceptions.
+                    System.out.println(e);
+                }
+
+                monthLabel[i].setText( monthFormat[i].format( calendarPanel.firstOfMonth() ));
+            }
+    }
+
+    /**
+     * CalMenuListener responds to events in the two popup menus that select
+     * the calendar systems to be used in the display.  It figures out which
+     * of the two menus the event occurred in and updates the corresponding
+     * element of the calendars[] array to match the new selection.
+     */
+    private class CalMenuListener implements ItemListener
+    {
+         public void itemStateChanged(ItemEvent e)
+         {
+            for (int i = 0; i < calMenu.length; i++)
+            {
+                if (e.getItemSelectable() == calMenu[i])
+                {
+                    // We found the menu that the event happened in.
+                    // Figure out which new calendar they selected.
+                    Calendar newCal = CALENDARS[ calMenu[i].getSelectedIndex() ].calendar;
+
+                    if (newCal != calendars[i])
+                    {
+                        // If any of the other menus are set to the same new calendar
+                        // we're about to use for this menu, set them to the current
+                        // calendar from *this* menu so we won't have two the same
+                        for (int j = 0; j < calendars.length; j++) {
+                            if (j != i && calendars[j] == newCal) {
+                                calendars[j] = calendars[i];
+                                calendarPanel.setCalendar(j, calendars[j]);
+                                monthFormat[j] = null;
+
+                                for (int k = 0; k < CALENDARS.length; k++) {
+                                    if (calendars[j] == CALENDARS[k].calendar) {
+                                        calMenu[j].select(k);
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        // Now update this menu to use the new calendar the user selected
+                        calendars[i] = newCal;
+                        calendarPanel.setCalendar(i, newCal);
+                        monthFormat[i] = null;
+
+                        updateMonthName();
+                    }
+                    break;
+                }
+            }
+         }
+    };
+
+    /**
+     * AddAction handles the next/previous year/month buttons...
+     */
+    private class AddAction implements ActionListener {
+        AddAction(int field, int amount) {
+            this.field = field;
+            this.amount = amount;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            calendarPanel.add(field, amount);
+            updateMonthName();
+        }
+
+        private int field, amount;
+    }
+
+    /**
+     * ColoredLabel is similar to java.awt.Label, with two differences:
+     *
+     *  - You can set its text color
+     *
+     *  - It draws text using drawString rather than using a host-specific
+     *    "Peer" object like AWT does.  On 1.2, using drawString gives
+     *    us Bidi reordering for free.
+     */
+    static private class ColoredLabel extends Component {
+        public ColoredLabel(String label) {
+            text = label;
+        }
+
+        public ColoredLabel(String label, Color c) {
+            text = label;
+            color = c;
+        }
+
+        public void setText(String label) {
+            text = label;
+            repaint();
+        }
+
+        public void setFont(Font f) {
+            font = f;
+            repaint();
+        }
+
+        public void paint(Graphics g) {
+            FontMetrics fm = g.getFontMetrics(font);
+
+            Rectangle bounds = getBounds();
+
+            g.setColor(color);
+            g.setFont(font);
+            g.drawString(text, fm.stringWidth("\u00a0"),
+                         bounds.height/2 + fm.getHeight()
+                         - fm.getAscent() + fm.getLeading()/2);
+        }
+
+        public Dimension getPreferredSize() {
+            return getMinimumSize();
+        }
+
+        public Dimension getMinimumSize() {
+            FontMetrics fm = getFontMetrics(font);
+
+            return new Dimension( fm.stringWidth(text) + 2*fm.stringWidth("\u00a0"),
+                                  fm.getHeight() + fm.getLeading()*2);
+        }
+
+        String text;
+        Color color = Color.black;
+        Font font = DemoUtility.labelFont;
+    }
+
+    /**
+     * Print out the error message while debugging this program.
+     */
+    public void errorText(String s)
+    {
+        if (DEBUG)
+        {
+            System.out.println(s);
+        }
+    }
+
+    class CalendarRec {
+        public CalendarRec(String nameStr, java.util.Calendar cal)
+        {
+            name = nameStr;
+            calendar = cal;
+        }
+
+        java.util.Calendar  calendar;
+        String              name;
+    };
+
+    private final CalendarRec[] CALENDARS = {
+        new CalendarRec("Gregorian Calendar",       new GregorianCalendar()),
+        new CalendarRec("Hebrew Calendar",          new HebrewCalendar()),
+        new CalendarRec("Islamic Calendar",         makeIslamic(false)),
+        new CalendarRec("Islamic Civil Calendar ",  makeIslamic(true)),
+        new CalendarRec("Buddhist Calendar",        new BuddhistCalendar()),
+        new CalendarRec("Japanese Calendar",        new JapaneseCalendar()),
+    };
+
+    static private final Calendar makeIslamic(boolean civil) {
+        IslamicCalendar cal = new IslamicCalendar();
+        cal.setCivil(civil);
+        return cal;
+    };
+
+    static final Color[] COLORS = { Color.blue, Color.black };
+}
+
diff --git a/src/com/ibm/demo/calendar/CalendarPanel.java b/src/com/ibm/demo/calendar/CalendarPanel.java
new file mode 100755
index 0000000..f9e3c8a
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarPanel.java
@@ -0,0 +1,364 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/calendar/Attic/CalendarPanel.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.calendar;
+
+import com.ibm.demo.*;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+//import java.util.*;
+import java.net.*;
+import java.io.*;
+
+import java.text.DateFormat;
+import java.util.SimpleTimeZone;
+import java.util.*;
+
+import com.ibm.util.*;
+import com.ibm.text.*;
+
+class CalendarPanel extends Canvas {
+
+    public CalendarPanel( Locale locale ) {
+        setLocale(locale);
+    }
+
+    public void setLocale(Locale locale) {
+        if (fDisplayLocale == null || !fDisplayLocale.equals(locale)) {
+            fDisplayLocale = locale;
+            dirty = true;
+
+            for (int i = 0; i < fCalendar.length; i++) {
+                if (fCalendar[i] != null) {
+                    fSymbols[i] = IBMCalendar.getDateFormatSymbols(fCalendar[i],
+                                                                   fDisplayLocale);
+                }
+            }
+            String lang = locale.getLanguage();
+            leftToRight = !(lang.equals("iw") || lang.equals("ar"));
+
+            repaint();
+        }
+    }
+
+    public void setDate(Date date) {
+        fStartOfMonth = date;
+        dirty = true;
+        repaint();
+    }
+
+    public void add(int field, int delta)
+    {
+        synchronized(fCalendar) {
+            fCalendar[0].setTime(fStartOfMonth);
+            fCalendar[0].add(field, delta);
+            fStartOfMonth = fCalendar[0].getTime();
+        }
+        dirty = true;
+        repaint();
+    }
+
+    public void setColor(int index, Color c) {
+        fColor[index] = c;
+        repaint();
+    }
+
+    public void setCalendar(int index, Calendar c) {
+        Date date = (fCalendar[index] == null) ? new Date()
+                                               : fCalendar[index].getTime();
+
+        fCalendar[index] = c;
+        fCalendar[index].setTime(date);
+
+        fSymbols[index] = IBMCalendar.getDateFormatSymbols(c, fDisplayLocale);
+        dirty = true;
+        repaint();
+    }
+
+    public Calendar getCalendar(int index) {
+        return fCalendar[index];
+    }
+
+    public Locale getDisplayLocale() {
+        return fDisplayLocale;
+    }
+
+    public Date firstOfMonth() {
+        return fStartOfMonth;
+    }
+
+    private Date startOfMonth(Date dateInMonth)
+    {
+        synchronized(fCalendar) {
+            fCalendar[0].setTime(dateInMonth);
+
+            int era = fCalendar[0].get(Calendar.ERA);
+            int year = fCalendar[0].get(Calendar.YEAR);
+            int month = fCalendar[0].get(Calendar.MONTH);
+
+            fCalendar[0].clear();
+            fCalendar[0].set(Calendar.ERA, era);
+            fCalendar[0].set(Calendar.YEAR, year);
+            fCalendar[0].set(Calendar.MONTH, month);
+            fCalendar[0].set(Calendar.DATE, 1);
+
+            return fCalendar[0].getTime();
+        }
+    }
+
+    private void calculate()
+    {
+        //
+        // As a workaround for JDK 1.1.3 and below, where Calendars and time
+        // zones are a bit goofy, always set my calendar's time zone to UTC.
+        // You would think I would want to do this in the "set" function above,
+        // but if I do that, the program hangs when this class is loaded,
+        // perhaps due to some sort of static initialization ordering problem.
+        // So I do it here instead.
+        //
+        fCalendar[0].setTimeZone(new SimpleTimeZone(0, "UTC"));
+
+        Calendar c = (Calendar)fCalendar[0].clone(); // Temporary copy
+
+        fStartOfMonth = startOfMonth(fStartOfMonth);
+
+        // Stash away a few useful constants for this calendar and display
+        minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
+        daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
+
+        firstDayOfWeek = Calendar.getInstance(fDisplayLocale).getFirstDayOfWeek();
+
+        // Stash away a Date for the start of this month
+
+        // Find the day of week of the first day in this month
+        c.setTime(fStartOfMonth);
+        firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
+        int firstWeek = c.get(Calendar.WEEK_OF_MONTH);
+
+        // Now find the # of days in the month
+        c.roll(Calendar.DATE, false);
+        daysInMonth = c.get(Calendar.DATE);
+
+        // Finally, find the end of the month, i.e. the start of the next one
+        c.roll(Calendar.DATE, true);
+        c.add(Calendar.MONTH, 1);
+        c.getTime();        // JDK 1.1.2 bug workaround
+        c.add(Calendar.SECOND, -1);
+        Date endOfMonth = c.getTime();
+        int lastWeek = c.get(Calendar.WEEK_OF_MONTH);
+
+        // Calculate the number of full or partial weeks in this month.
+        numWeeks = lastWeek - firstWeek + 1;
+
+        dirty = false;
+    }
+
+    static final int XINSET = 4;
+    static final int YINSET = 2;
+
+    /*
+     * Convert from the day number within a month (1-based)
+     * to the cell coordinates on the calendar (0-based)
+     */
+    private void dateToCell(int date, Point pos)
+    {
+        int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
+        if (firstDayInMonth < firstDayOfWeek) {
+            cell += daysInWeek;
+        }
+
+        pos.x = cell % daysInWeek;
+        pos.y = cell / daysInWeek;
+    }
+    private Point dateToCell(int date) {
+        Point p = new Point(0,0);
+        dateToCell(date, p);
+        return p;
+    }
+
+    public void paint(Graphics g) {
+
+        if (dirty) {
+            calculate();
+        }
+
+        Point cellPos = new Point(0,0);     // Temporary variable
+        Dimension d = this.getSize();
+
+        g.setColor(Color.lightGray);
+        g.fillRect(0,0,d.width,d.height);
+
+        // Draw the day names at the top
+        g.setColor(Color.black);
+        g.setFont(DemoUtility.labelFont);
+        FontMetrics fm = g.getFontMetrics();
+        int labelHeight = fm.getHeight() + YINSET * 2;
+
+        int v = fm.getAscent() + YINSET;
+        for (int i = 0; i < daysInWeek; i++) {
+            int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek + 1;
+            String dayName = fSymbols[0].getWeekdays()[dayNum];
+
+
+            double h;
+            if (leftToRight) {
+                h = d.width*(i + 0.5) / daysInWeek;
+            } else {
+                h = d.width*(daysInWeek - i - 0.5) / daysInWeek;
+            }
+            h -= fm.stringWidth(dayName) / 2;
+
+            g.drawString(dayName, (int)h, v);
+        }
+
+        double cellHeight = (d.height - labelHeight - 1) / numWeeks;
+        double cellWidth = (double)(d.width - 1) / daysInWeek;
+
+        // Draw a white background in the part of the calendar
+        // that displays this month.
+        // First figure out how much of the first week should be shaded.
+        {
+            g.setColor(Color.white);
+            dateToCell(1, cellPos);
+            int width = (int)(cellPos.x*cellWidth);  // Width of unshaded area
+
+            if (leftToRight) {
+                g.fillRect((int)(width), labelHeight ,
+                           d.width - width, (int)cellHeight);
+            } else {
+                g.fillRect(0, labelHeight ,
+                           d.width - width, (int)cellHeight);
+            }
+
+            // All of the intermediate weeks get shaded completely
+            g.fillRect(0, (int)(labelHeight + cellHeight),
+                        d.width, (int)(cellHeight * (numWeeks - 2)));
+
+            // Now figure out the last week.
+            dateToCell(daysInMonth, cellPos);
+            width = (int)((cellPos.x+1)*cellWidth);  // Width of shaded area
+
+            if (leftToRight) {
+                g.fillRect(0, (int)(labelHeight + (numWeeks-1) * cellHeight),
+                           width, (int)cellHeight);
+            } else {
+                g.fillRect(d.width - width, (int)(labelHeight + (numWeeks-1) * cellHeight),
+                           width, (int)cellHeight);
+            }
+
+        }
+        // Draw the X/Y grid lines
+        g.setColor(Color.black);
+        for (int i = 0; i <= numWeeks; i++) {
+            int y = (int)(labelHeight + i * cellHeight);
+            g.drawLine(0, y, d.width - 1, y);
+        }
+        for (int i = 0; i <= daysInWeek; i++) {
+            int x = (int)(i * cellWidth);
+            g.drawLine(x, labelHeight, x, d.height - 1);
+        }
+
+        // Now loop through all of the days in the month, figure out where
+        // they go in the grid, and draw the day # for each one
+
+        // Figure out the date of the first cell in the calendar display
+        int cell = (1 + firstDayInMonth - firstDayOfWeek - minDay);
+        if (firstDayInMonth < firstDayOfWeek) {
+            cell += daysInWeek;
+        }
+
+        Calendar c = (Calendar)fCalendar[0].clone();
+        c.setTime(fStartOfMonth);
+        c.add(Calendar.DATE, -cell);
+
+        StringBuffer buffer = new StringBuffer();
+
+        for (int row = 0; row < numWeeks; row++) {
+            for (int col = 0; col < daysInWeek; col++) {
+
+                g.setFont(DemoUtility.numberFont);
+                g.setColor(Color.black);
+                fm = g.getFontMetrics();
+
+                int cellx;
+                if (leftToRight) {
+                    cellx = (int)((col) * cellWidth);
+                } else {
+                    cellx = (int)((daysInWeek - col - 1) * cellWidth);
+                }
+
+                int celly = (int)(row * cellHeight + labelHeight);
+
+                for (int i = 0; i < 2; i++) {
+                    fCalendar[i].setTime(c.getTime());
+
+                    int date = fCalendar[i].get(Calendar.DATE);
+                    buffer.setLength(0);
+                    buffer.append(date);
+                    String dayNum = buffer.toString();
+
+                    int x;
+
+                    if (leftToRight) {
+                        x = cellx + (int)cellWidth - XINSET - fm.stringWidth(dayNum);
+                    } else {
+                        x = cellx + XINSET;
+                    }
+                    int y = celly + + fm.getAscent() + YINSET + i * fm.getHeight();
+
+                    if (fColor[i] != null) {
+                        g.setColor(fColor[i]);
+                    }
+                    g.drawString(dayNum, x, y);
+
+                    if (date == 1 || row == 0 && col == 0) {
+                        g.setFont(DemoUtility.numberFont);
+                        String month = fSymbols[i].getMonths()[
+                                            fCalendar[i].get(Calendar.MONTH)];
+
+                        if (leftToRight) {
+                            x = cellx + XINSET;
+                        } else {
+                            x = cellx + (int)cellWidth - XINSET - fm.stringWidth(month);
+                        }
+                        g.drawString(month, x, y);
+                    }
+                }
+
+                c.add(Calendar.DATE, 1);
+            }
+        }
+    }
+
+    // Important state variables
+    private Calendar[]          fCalendar = new Calendar[4];
+    private Color[]             fColor = new Color[4];
+
+    private Locale              fDisplayLocale;
+    private DateFormatSymbols[] fSymbols = new DateFormatSymbols[4];
+
+    private Date                fStartOfMonth = new Date();     // 00:00:00 on first day of month
+
+    // Cached calculations to make drawing faster.
+    private transient int       minDay;           // Minimum legal day #
+    private transient int       daysInWeek;       // # of days in a week
+    private transient int       firstDayOfWeek;   // First day to display in week
+    private transient int       numWeeks;         // # full or partial weeks in month
+    private transient int       daysInMonth;      // # days in this month
+    private transient int       firstDayInMonth;  // Day of week of first day in month
+    private transient boolean   leftToRight;
+
+    private transient boolean dirty = true;
+}
diff --git a/src/com/ibm/demo/holiday/HolidayBorderPanel.java b/src/com/ibm/demo/holiday/HolidayBorderPanel.java
new file mode 100755
index 0000000..d838142
--- /dev/null
+++ b/src/com/ibm/demo/holiday/HolidayBorderPanel.java
@@ -0,0 +1,550 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1997-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/holiday/Attic/HolidayBorderPanel.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.holiday;
+
+import com.ibm.demo.*;
+import java.awt.*;
+
+/**
+ * Various graphical borders. The border itself is a Panel so that it can
+ * contain other Components (i.e. it borders something). You use the
+ * HolidayBorderPanel like any other Panel: you set the layout that you prefer and
+ * add Components to it. Beware that a null layout does not obey the insets
+ * of the panel so if you use null layouts, adjust your measurements to
+ * handle the border by calling insets().
+ *
+ * @author  Andy Clark, Taligent Inc.
+ * @version 1.0
+ */
+public class HolidayBorderPanel extends Panel {
+    // Constants
+
+    /** Solid border. */
+    public final static int SOLID = 0;
+    /** A raised border. */
+    public final static int RAISED = 1;
+    /** A lowered border. */
+    public final static int LOWERED = 2;
+    /** An etched in border. */
+    public final static int IN = 3;
+    /** An etched out border. */
+    public final static int OUT = 4;
+
+    /** Left alignment. */
+    public final static int LEFT = 0;
+    /** Center alignment. */
+    public final static int CENTER = 1;
+    /** Right alignment. */
+    public final static int RIGHT = 2;
+
+    /** Default style (IN). */
+    public final static int DEFAULT_STYLE = IN;
+    /** Default thickness (10). */
+    public final static int DEFAULT_THICKNESS = 10;
+    /** Default thickness for solid borders (4). */
+    public final static int DEFAULT_SOLID_THICKNESS = 4;
+    /** Default thickness for raised borders (2). */
+    public final static int DEFAULT_RAISED_THICKNESS = 2;
+    /** Default thickness for lowered borders (2). */
+    public final static int DEFAULT_LOWERED_THICKNESS = 2;
+    /** Default thickness for etched-in borders (10). */
+    public final static int DEFAULT_IN_THICKNESS = 10;
+    /** Default thickness for etched-out borders (10). */
+    public final static int DEFAULT_OUT_THICKNESS = 10;
+    /** Default gap between border and contained component (5). */
+    public final static int DEFAULT_GAP = 5;
+    /** Default color (black). Applies to SOLID and etched borders. */
+    public final static Color DEFAULT_COLOR = Color.black;
+
+    /** Default font (TimesRoman,PLAIN,14). Only applies to etched borders. */
+    public final static Font DEFAULT_FONT = new Font("TimesRoman", Font.PLAIN, 14);
+    /** Default alignment (LEFT). Only applies to etched borders. */
+    public final static int DEFAULT_ALIGNMENT = LEFT;
+
+    // Data
+    private int style;
+    private int thickness;
+    private int gap;
+    private Color color;
+
+    private Font font;
+    private String text;
+    private int alignment;
+
+    /**
+     * Constructor. Makes default border.
+     */
+    public HolidayBorderPanel() {
+
+        // initialize data
+        style       = DEFAULT_STYLE;
+        thickness   = DEFAULT_THICKNESS;
+        gap         = DEFAULT_GAP;
+        color       = DEFAULT_COLOR;
+
+        text        = null;
+        font        = DEFAULT_FONT;
+        alignment   = DEFAULT_ALIGNMENT;
+
+        }
+
+    /**
+     * Constructor. Makes an etched IN border with given text caption.
+     *
+     * @param text  Text caption
+     */
+    public HolidayBorderPanel(String text) {
+        this();
+
+        style = IN;
+        this.text = text;
+        }
+
+    /**
+     * Constructor. Makes SOLID border with color and thickness given.
+     *
+     * @param color     The color for the border.
+     * @param thickness The thickness of the border.
+     */
+    public HolidayBorderPanel(Color color, int thickness) {
+        this();
+
+        style = SOLID;
+        this.color = color;
+        this.thickness = thickness;
+        }
+
+    /**
+     * Constructor. Makes a border of the given style with the default
+     * thickness for that style.
+     *
+     * @param style The style for this border.
+     */
+    public HolidayBorderPanel(int style) {
+        this();
+
+        // set thickness appropriate to this style
+        int thickness;
+        switch (style) {
+            case SOLID: thickness = DEFAULT_SOLID_THICKNESS; break;
+            case RAISED: thickness = DEFAULT_RAISED_THICKNESS; break;
+            case LOWERED: thickness = DEFAULT_LOWERED_THICKNESS; break;
+            case IN: thickness = DEFAULT_IN_THICKNESS; break;
+            case OUT: thickness = DEFAULT_OUT_THICKNESS; break;
+            default:
+                thickness = DEFAULT_THICKNESS;
+            }
+
+        this.style = style;
+        this.thickness = thickness;
+        }
+
+    /**
+     * Constructor. Makes border with given style and thickness.
+     *
+     * @param style     The style for this border.
+     * @param thickness The thickness for this border.
+     */
+    public HolidayBorderPanel(int style, int thickness) {
+        this();
+
+        this.style = style;
+        this.thickness = thickness;
+        }
+
+    /**
+     * Returns the insets of this panel..
+     */
+    public Insets insets() {
+        int adjustment = 0;
+
+        // adjust for text string
+        if (style == IN || style == OUT) {
+            if (text != null && text.length() > 0) {
+                try {
+                    // set font and get info
+                    int height = getGraphics().getFontMetrics(font).getHeight();
+                    if (height > thickness)
+                        adjustment = height - thickness;
+                    }
+                catch (Exception e) {
+                    // nothing: just in case there is no graphics context
+                    //   at the beginning.
+                    }
+                }
+            }
+
+        // return appropriate insets
+        int dist = thickness + gap;
+        return new Insets(dist + adjustment, dist, dist, dist);
+        }
+
+    /**
+     * Sets the style of the border
+     *
+     * @param style The new style.
+     */
+    public HolidayBorderPanel setStyle(int style) {
+
+        // set the style and re-layout the panel
+        this.style = style;
+        layout();
+        repaint();
+
+        return this;
+        }
+
+    /**
+     * Gets the style of the border
+     */
+    public int getStyle() {
+
+        return style;
+        }
+
+    /**
+     * Sets the thickness of the border.
+     *
+     * @param thickness The new thickness
+     */
+    public HolidayBorderPanel setThickness(int thickness) {
+
+        if (thickness > 0) {
+            this.thickness = thickness;
+            layout();
+            repaint();
+            }
+
+        return this;
+        }
+
+    /**
+     * Gets the thickness of the border.
+     */
+    public int getThickness() {
+
+        return thickness;
+        }
+
+    /**
+     * Sets the gap between the border and the contained Component.
+     *
+     * @param gap The new gap, in pixels.
+     */
+    public HolidayBorderPanel setGap(int gap) {
+
+        if (gap > -1) {
+            this.gap = gap;
+            layout();
+            repaint();
+            }
+
+        return this;
+        }
+
+    /**
+     * Gets the gap between the border and the contained Component.
+     */
+    public int getGap() {
+
+        return gap;
+        }
+
+    /**
+     * Sets the current color for SOLID borders and the caption text
+     * color for etched borders.
+     *
+     * @param color The new color.
+     */
+    public HolidayBorderPanel setColor(Color color) {
+
+        this.color = color;
+        if (style == SOLID || style == IN || style == OUT)
+            repaint();
+
+        return this;
+        }
+
+    /**
+     * Gets the current color for SOLID borders and the caption
+     * text color for etched borders.
+     */
+    public Color getColor() {
+
+        return color;
+        }
+
+    /**
+     * Sets the font. Only applies to etched borders.
+     */
+    public HolidayBorderPanel setTextFont(Font font) {
+
+        // set font
+        if (font != null) {
+            this.font = font;
+            if (style == IN || style == OUT) {
+                layout();
+                repaint();
+                }
+            }
+
+        return this;
+        }
+
+    /**
+     * Gets the font of the text. Only applies to etched borders.
+     */
+    public Font getTextFont() {
+
+        return font;
+        }
+
+    /**
+     * Sets the text. Only applies to etched borders.
+     *
+     * @param text  The new text.
+     */
+    public HolidayBorderPanel setText(String text) {
+
+        this.text = text;
+        if (style == IN || style == OUT) {
+            layout();
+            repaint();
+            }
+
+        return this;
+        }
+
+    /**
+     * Gets the text. Only applies to etched borders.
+     */
+    public String getText() {
+
+        return text;
+        }
+
+    /**
+     * Sets the text alignment. Only applies to etched borders.
+     *
+     * @param alignment The new alignment.
+     */
+    public HolidayBorderPanel setAlignment(int alignment) {
+
+        this.alignment = alignment;
+        if (style == IN || style == OUT) {
+            layout();
+            repaint();
+            }
+
+        return this;
+        }
+
+    /**
+     * Gets the text alignment.
+     */
+    public int getAlignment() {
+
+        return alignment;
+        }
+
+    /**
+     * Repaints the border.
+     *
+     * @param g The graphics context.
+     */
+    public void paint(Graphics g) {
+
+        // get current dimensions
+        Dimension size = size();
+        int width = size.width;
+        int height = size.height;
+
+        // set colors
+        Color light = getBackground().brighter().brighter().brighter();
+        Color dark = getBackground().darker().darker().darker();
+
+        // Draw border
+        switch (style) {
+            case RAISED:    // 3D Border (in or out)
+            case LOWERED:
+                Color topleft = null;
+                Color bottomright = null;
+
+                // set colors
+                if (style == RAISED) {
+                    topleft = light;
+                    bottomright = dark;
+                    }
+                else {
+                    topleft = dark;
+                    bottomright = light;
+                    }
+
+                // draw border
+                g.setColor(topleft);
+                for (int i = 0; i < thickness; i++) {
+                    g.drawLine(i, i, width - i - 2, i);
+                    g.drawLine(i, i + 1, i, height - i - 1);
+                    }
+                g.setColor(bottomright);
+                for (int i = 0; i < thickness; i++) {
+                    g.drawLine(i + 1, height - i - 1, width - i - 1, height - i - 1);
+                    g.drawLine(width - i - 1, i, width - i - 1, height - i - 2);
+                    }
+                break;
+
+            case IN:    // Etched Border (in or out)
+            case OUT:
+                int adjust1 = 0;
+                int adjust2 = 0;
+
+                // set font and get info
+                Font oldfont = g.getFont();
+                g.setFont(font);
+                FontMetrics fm = g.getFontMetrics();
+                int ascent = fm.getAscent();
+
+                // set adjustment
+                if (style == IN)
+                    adjust1 = 1;
+                else
+                    adjust2 = 1;
+
+                // Calculate adjustment for text
+                int adjustment = 0;
+                if (text != null && text.length() > 0) {
+                    if (ascent > thickness)
+                        adjustment = (ascent - thickness) / 2;
+                    }
+
+                // The adjustment is there so that we always draw the
+                // light rectangle first. Otherwise, your eye picks up
+                // the discrepancy where the light rect. passes over
+                // the darker rect.
+                int x = thickness / 2;
+                int y = thickness / 2 + adjustment;
+                int w = width - thickness - 1;
+                int h = height - thickness - 1 - adjustment;
+
+                // draw rectangles
+                g.setColor(light);
+                g.drawRect(x + adjust1, y + adjust1, w, h);
+                g.setColor(dark);
+                g.drawRect(x + adjust2, y + adjust2, w, h);
+
+                // draw text, if applicable
+                if (text != null && text.length() > 0) {
+                    // calculate drawing area
+                    int fontheight = fm.getHeight();
+                    int strwidth = fm.stringWidth(text);
+
+                    int textwidth = width - 2 * (thickness + 5);
+                    if (strwidth > textwidth)
+                        strwidth = textwidth;
+
+                    // calculate offset for alignment
+                    int offset;
+                    switch (alignment) {
+                        case CENTER:
+                            offset = (width - strwidth) / 2;
+                            break;
+                        case RIGHT:
+                            offset = width - strwidth - thickness - 5;
+                            break;
+                        case LEFT:
+                        default: // assume left alignment if invalid
+                            offset = thickness + 5;
+                            break;
+                        }
+
+                    // clear drawing area and set clipping region
+                    g.clearRect(offset - 5, 0, strwidth  + 10, fontheight);
+                    g.clipRect(offset, 0, strwidth, fontheight);
+
+                    // draw text
+                    g.setColor(color);
+                    g.drawString(text, offset, ascent);
+
+                    // restore old clipping area
+                    g.clipRect(0, 0, width, height);
+                    }
+
+                g.setFont(oldfont);
+                break;
+
+            case SOLID:
+            default: // assume SOLID
+                g.setColor(color);
+                for (int i = 0; i < thickness; i++)
+                    g.drawRect(i, i, width - 2 * i - 1, height - 2 * i - 1);
+            }
+
+        }
+
+    /**
+     * Returns the settings of this HolidayBorderPanel instance as a string.
+     */
+    public String toString() {
+        StringBuffer str = new StringBuffer("HolidayBorderPanel[");
+
+        // style
+        str.append("style=");
+        switch (style) {
+            case SOLID: str.append("SOLID"); break;
+            case RAISED: str.append("RAISED"); break;
+            case LOWERED: str.append("LOWERED"); break;
+            case IN: str.append("IN"); break;
+            case OUT: str.append("OUT"); break;
+            default: str.append("unknown");
+            }
+        str.append(",");
+
+        // thickness
+        str.append("thickness=");
+        str.append(thickness);
+        str.append(",");
+
+        // gap
+        str.append("gap=");
+        str.append(gap);
+        str.append(",");
+
+        // color
+        str.append(color);
+        str.append(",");
+
+        // font
+        str.append(font);
+        str.append(",");
+
+        // text
+        str.append("text=");
+        str.append(text);
+        str.append(",");
+
+        // alignment
+        str.append("alignment=");
+        switch (alignment) {
+            case LEFT: str.append("LEFT"); break;
+            case CENTER: str.append("CENTER"); break;
+            case RIGHT: str.append("RIGHT"); break;
+            default: str.append("unknown");
+            }
+
+        str.append("]");
+
+        return str.toString();
+        }
+
+    }
+
diff --git a/src/com/ibm/demo/holiday/HolidayCalendarDemo.java b/src/com/ibm/demo/holiday/HolidayCalendarDemo.java
new file mode 100755
index 0000000..cb438ef
--- /dev/null
+++ b/src/com/ibm/demo/holiday/HolidayCalendarDemo.java
@@ -0,0 +1,706 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/holiday/Attic/HolidayCalendarDemo.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.holiday;
+
+import com.ibm.demo.*;
+import java.applet.Applet;
+import java.awt.*;
+import java.util.*;
+import java.net.*;
+import java.io.*;
+
+import java.text.SimpleDateFormat;
+import java.text.DateFormatSymbols;
+import java.util.SimpleTimeZone;
+import java.util.*;
+
+import com.ibm.util.*;
+
+/**
+ * CalendarDemo demonstrates how Calendar works.
+ */
+public class HolidayCalendarDemo extends DemoApplet
+{
+    /**
+     * The main function which defines the behavior of the CalendarDemo
+     * applet when an applet is started.
+     */
+    public static void main(String argv[]) {
+
+        new HolidayCalendarDemo().showDemo();
+    }
+
+    /* This creates a CalendarFrame for the demo applet. */
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new CalendarFrame(applet);
+    }
+
+	/**
+	* A Frame is a top-level window with a title. The default layout for a frame
+	* is BorderLayout.  The CalendarFrame class defines the window layout of
+	* CalendarDemo.
+	*/
+	private static class CalendarFrame extends Frame
+	{
+    	private static final String creditString = "";
+
+    	private static final boolean DEBUG = false;
+
+    	private Locale curLocale = Locale.US;
+
+    	private DemoApplet applet;
+
+    	private static final Locale[] calendars = {
+        	//new Locale("de","AT"),
+        	Locale.CANADA,
+        	Locale.CANADA_FRENCH,
+        	Locale.FRANCE,
+        	Locale.GERMANY,
+        	new Locale("iw","IL"),
+        	new Locale("el","GR"),
+        	//new Locale("es","MX"),
+        	Locale.UK,
+        	Locale.US,
+    	};
+    	private static final Locale[] displays = {
+        	Locale.CANADA,
+        	Locale.UK,
+        	Locale.US,
+        	Locale.FRANCE,
+        	Locale.CANADA_FRENCH,
+        	//new Locale("de","AT"),
+        	Locale.GERMAN,
+        	new Locale("el","GR"),
+        	//new Locale("iw","IL"),
+        	new Locale("es","MX"),
+    	};
+
+    	/**
+    	* Constructs a new CalendarFrame that is initially invisible.
+    	*/
+    	public CalendarFrame(DemoApplet applet)
+    	{
+        	super("Calendar Demo");
+        	this.applet = applet;
+        	init();
+        	start();
+    	}
+
+    	/**
+    	* Initializes the applet. You never need to call this directly, it
+    	* is called automatically by the system once the applet is created.
+    	*/
+    	public void init()
+    	{
+        	// Get G7 locales only for demo purpose. To get all the locales
+        	// supported, switch to calling Calendar.getAvailableLocales().
+        	// commented
+        	locales = displays;
+
+        	buildGUI();
+    	}
+
+    	//------------------------------------------------------------
+    	// package private
+    	//------------------------------------------------------------
+    	void addWithFont(Container container, Component foo, Font font) {
+        	if (font != null)
+            	foo.setFont(font);
+        	container.add(foo);
+    	}
+
+    	/**
+    	* Called to start the applet. You never need to call this method
+    	* directly, it is called when the applet's document is visited.
+    	*/
+    	public void start()
+    	{
+        	// do nothing
+    	}
+
+    	private Choice          localeMenu;
+    	private Choice          displayMenu;
+    	private Locale[]        locales;
+
+    	private Label           monthLabel;
+    	private Button          prevYear;
+    	private Button          prevMonth;
+    	private Button          gotoToday;
+    	private Button          nextMonth;
+    	private Button          nextYear;
+    	private CalendarPanel   calendarPanel;
+
+    	private static final Locale kFirstLocale = Locale.US;
+
+    	private static void add(Container container, Component component,
+                            	GridBagLayout g, GridBagConstraints c)
+    	{
+        	g.setConstraints(component, c);
+        	container.add(component);
+    	}
+
+    	public void buildGUI()
+    	{
+        	setBackground(DemoUtility.bgColor);
+        	setLayout(new BorderLayout(10,10));
+
+        	// Label for the demo's title
+        	Label titleLabel = new Label("Calendar Demo", Label.CENTER);
+        	titleLabel.setFont(DemoUtility.titleFont);
+
+        	// Label for the current month name
+        	monthLabel = new Label("", Label.LEFT);
+        	monthLabel.setFont(new Font(DemoUtility.titleFont.getName(),
+                                    	DemoUtility.titleFont.getStyle(),
+                                    	(DemoUtility.titleFont.getSize() * 3)/2));
+
+        	// Make the locale popup menus
+        	localeMenu= new Choice();
+        	int selectMe = 0;
+        	for (int i = 0; i < calendars.length; i++) {
+            	if (i > 0 &&
+                    	calendars[i].getCountry().equals(calendars[i-1].getCountry()) ||
+                	i < calendars.length - 1 &&
+                    	calendars[i].getCountry().equals(calendars[i+1].getCountry()))
+            	{
+                	localeMenu.addItem(calendars[i].getDisplayCountry() + " (" +
+                                	calendars[i].getDisplayLanguage() + ")");
+            	} else {
+                	localeMenu.addItem( calendars[i].getDisplayCountry() );
+            	}
+            	if (calendars[i].equals(kFirstLocale)) {
+                	selectMe = i;
+            	}
+        	}
+        	localeMenu.setBackground(DemoUtility.choiceColor);
+        	localeMenu.select(selectMe);
+
+        	displayMenu = new Choice();
+        	for (int i = 0; i < locales.length; i++) {
+            	if (i > 0 &&
+                    	locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
+                	i < locales.length - 1 &&
+                    	locales[i].getLanguage().equals(locales[i+1].getLanguage()))
+            	{
+                	displayMenu.addItem( locales[i].getDisplayName() );
+            	} else {
+                	displayMenu.addItem( locales[i].getDisplayLanguage());
+            	}
+        	}
+        	displayMenu.setBackground(DemoUtility.choiceColor);
+        	displayMenu.select(kFirstLocale.getDisplayName());
+
+        	// Make all the next/previous/today buttons
+        	prevYear = new Button("<<");
+        	prevMonth = new Button("<");
+        	gotoToday = new Button("Today");
+        	nextMonth = new Button(">");
+        	nextYear = new Button(">>");
+
+        	// The month name and the control buttons are bunched together
+        	Panel monthPanel = new Panel();
+        	{
+            	GridBagLayout g = new GridBagLayout();
+            	GridBagConstraints c = new GridBagConstraints();
+            	monthPanel.setLayout(g);
+
+            	c.weightx = 1;
+            	c.weighty = 1;
+
+            	c.gridwidth = 1;
+            	c.fill = GridBagConstraints.HORIZONTAL;
+            	c.gridwidth = GridBagConstraints.REMAINDER;
+            	add(monthPanel, monthLabel, g, c);
+
+            	c.gridwidth = 1;
+            	add(monthPanel, prevYear, g, c);
+            	add(monthPanel, prevMonth, g, c);
+            	add(monthPanel, gotoToday, g, c);
+            	add(monthPanel, nextMonth, g, c);
+            	c.gridwidth = GridBagConstraints.REMAINDER;
+            	add(monthPanel, nextYear, g, c);
+        	}
+
+        	// Stick the menu and buttons in a little "control panel"
+        	Panel menuPanel = new Panel();
+        	{
+            	GridBagLayout g = new GridBagLayout();
+            	GridBagConstraints c = new GridBagConstraints();
+            	menuPanel.setLayout(g);
+
+            	c.weightx = 1;
+            	c.weighty = 1;
+
+            	c.fill = GridBagConstraints.HORIZONTAL;
+
+            	c.gridwidth = GridBagConstraints.RELATIVE;
+            	Label l1 = new Label("Holidays");
+            	l1.setFont(DemoUtility.labelFont);
+            	add(menuPanel, l1, g, c);
+
+            	c.gridwidth = GridBagConstraints.REMAINDER;
+            	add(menuPanel, localeMenu, g, c);
+
+            	c.gridwidth = GridBagConstraints.RELATIVE;
+            	Label l2 = new Label("Display:");
+            	l2.setFont(DemoUtility.labelFont);
+            	add(menuPanel, l2, g, c);
+
+            	c.gridwidth = GridBagConstraints.REMAINDER;
+            	add(menuPanel, displayMenu, g, c);
+        	}
+
+        	// The title, buttons, etc. go in a panel at the top of the window
+        	Panel topPanel = new Panel();
+        	{
+            	topPanel.setLayout(new BorderLayout());
+
+            	//topPanel.add("North", titleLabel);
+            	topPanel.add("Center", monthPanel);
+            	topPanel.add("East", menuPanel);
+        	}
+        	add("North", topPanel);
+
+        	// The copyright notice goes at the bottom of the window
+        	Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
+        	copyright.setFont(DemoUtility.creditFont);
+        	add("South", copyright);
+
+        	// Now create the big calendar panel and stick it in the middle
+        	calendarPanel = new CalendarPanel( kFirstLocale );
+        	add("Center", calendarPanel);
+
+        	updateMonthName();
+    	}
+
+    	/**
+    	* Called if an action occurs in the CalendarFrame object.
+    	*/
+    	public boolean action(Event evt, Object obj)
+    	{
+        	// *** Button events are handled here.
+        	boolean handled = false;
+
+        	if (evt.target instanceof Button) {
+            	if (evt.target == nextMonth) {
+                	calendarPanel.add(Calendar.MONTH, +1);
+                	handled = true;
+            	}
+            	else
+            	if (evt.target == prevMonth) {
+                	calendarPanel.add(Calendar.MONTH, -1);
+                	handled = true;
+            	}
+            	else
+            	if (evt.target == prevYear) {
+                	calendarPanel.add(Calendar.YEAR, -1);
+                	handled = true;
+            	}
+            	else
+            	if (evt.target == nextYear) {
+                	calendarPanel.add(Calendar.YEAR, +1);
+                	handled = true;
+            	}
+            	else
+            	if (evt.target == gotoToday) {
+                	calendarPanel.set( new Date() );
+                	handled = true;
+            	}
+            	if (handled) {
+                	updateMonthName();
+            	}
+        	}
+        	return handled || super.action(evt, obj);
+    	}
+
+    	private void updateMonthName()
+    	{
+        	SimpleDateFormat f = new SimpleDateFormat("MMMM yyyyy",
+                                                    	calendarPanel.getDisplayLocale());
+        	f.setCalendar(calendarPanel.getCalendar());
+        	f.setTimeZone(new SimpleTimeZone(0, "UTC"));        // JDK 1.1.2 workaround
+        	monthLabel.setText( f.format( calendarPanel.firstOfMonth() ));
+    	}
+
+    	/**
+    	* Handles the event. Returns true if the event is handled and should not
+    	* be passed to the parent of this component. The default event handler
+    	* calls some helper methods to make life easier on the programmer.
+    	*/
+    	public boolean handleEvent(Event evt)
+    	{
+        	if (evt.id == Event.ACTION_EVENT && evt.target == localeMenu) {
+            	calendarPanel.setCalendarLocale(calendars[localeMenu.getSelectedIndex()]);
+            	updateMonthName();
+            	return true;
+        	}
+        	if (evt.id == Event.ACTION_EVENT && evt.target == displayMenu) {
+            	calendarPanel.setDisplayLocale(locales[displayMenu.getSelectedIndex()]);
+            	updateMonthName();
+            	return true;
+        	}
+        	else
+        	if (evt.id == Event.WINDOW_DESTROY && evt.target == this) {
+            	this.hide();
+            	this.dispose();
+
+            	if (applet != null) {
+            	applet.demoClosed();
+            	} else {
+                	System.exit(0);
+            	}
+            	return true;
+        	}
+        	return super.handleEvent(evt);
+    	}
+
+    	/**
+    	* Print out the error message while debugging this program.
+    	*/
+    	public void errorText(String s)
+    	{
+        	if (DEBUG)
+        	{
+            	System.out.println(s);
+        	}
+    	}
+	}
+
+
+	private static class CalendarPanel extends Canvas {
+
+    	public CalendarPanel( Locale locale ) {
+        	set(locale, locale, new Date());
+    	}
+
+    	public void setCalendarLocale(Locale locale) {
+        	set(locale, fDisplayLocale, fCalendar.getTime());
+    	}
+
+    	public void setDisplayLocale(Locale locale) {
+        	set(fCalendarLocale, locale, fCalendar.getTime());
+    	}
+
+    	public void set(Date date) {
+        	set(fCalendarLocale, fDisplayLocale, date);
+    	}
+
+    	public void set(Locale loc, Locale display, Date date)
+    	{
+        	if (fCalendarLocale == null || !loc.equals(fCalendarLocale)) {
+            	fCalendarLocale = loc;
+            	fCalendar = Calendar.getInstance(fCalendarLocale);
+            	fAllHolidays = Holiday.getHolidays(fCalendarLocale);
+        	}
+        	if (fDisplayLocale == null || !display.equals(fDisplayLocale)) {
+            	fDisplayLocale = display;
+            	fSymbols = new DateFormatSymbols(fDisplayLocale);
+        	}
+
+        	fStartOfMonth = date;
+
+        	dirty = true;
+        	repaint();
+    	}
+
+    	public void add(int field, int delta)
+    	{
+        	synchronized(fCalendar) {
+            	fCalendar.setTime(fStartOfMonth);
+            	fCalendar.add(field, delta);
+            	fStartOfMonth = fCalendar.getTime();
+        	}
+        	dirty = true;
+        	repaint();
+    	}
+
+    	public Calendar getCalendar() {
+        	return fCalendar;
+    	}
+
+    	public Locale getCalendarLocale() {
+        	return fCalendarLocale;
+    	}
+
+    	public Locale getDisplayLocale() {
+        	return fDisplayLocale;
+    	}
+
+
+    	public Date firstOfMonth() {
+        	return fStartOfMonth;
+    	}
+
+    	private Date startOfMonth(Date dateInMonth)
+    	{
+        	synchronized(fCalendar) {
+            	fCalendar.setTime(dateInMonth);             // TODO: synchronization
+
+            	int era = fCalendar.get(Calendar.ERA);
+            	int year = fCalendar.get(Calendar.YEAR);
+            	int month = fCalendar.get(Calendar.MONTH);
+
+            	fCalendar.clear();
+            	fCalendar.set(Calendar.ERA, era);
+            	fCalendar.set(Calendar.YEAR, year);
+            	fCalendar.set(Calendar.MONTH, month);
+            	fCalendar.set(Calendar.DATE, 1);
+
+            	return fCalendar.getTime();
+        	}
+    	}
+
+    	private void calculate()
+    	{
+        	//
+        	// As a workaround for JDK 1.1.3 and below, where Calendars and time
+        	// zones are a bit goofy, always set my calendar's time zone to UTC.
+        	// You would think I would want to do this in the "set" function above,
+        	// but if I do that, the program hangs when this class is loaded,
+        	// perhaps due to some sort of static initialization ordering problem.
+        	// So I do it here instead.
+        	//
+        	fCalendar.setTimeZone(new SimpleTimeZone(0, "UTC"));
+
+        	Calendar c = (Calendar)fCalendar.clone(); // Temporary copy
+
+        	fStartOfMonth = startOfMonth(fStartOfMonth);
+
+        	// Stash away a few useful constants for this calendar and display
+        	minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
+        	daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
+
+        	firstDayOfWeek = Calendar.getInstance(fDisplayLocale).getFirstDayOfWeek();
+
+        	// Stash away a Date for the start of this month
+
+        	// Find the day of week of the first day in this month
+        	c.setTime(fStartOfMonth);
+        	firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
+
+        	// Now find the # of days in the month
+        	c.roll(Calendar.DATE, false);
+        	daysInMonth = c.get(Calendar.DATE);
+
+        	// Finally, find the end of the month, i.e. the start of the next one
+        	c.roll(Calendar.DATE, true);
+        	c.add(Calendar.MONTH, 1);
+        	c.getTime();        // JDK 1.1.2 bug workaround
+        	c.add(Calendar.SECOND, -1);
+        	Date endOfMonth = c.getTime();
+
+        	//
+        	// Calculate the number of full or partial weeks in this month.
+        	// To do this I can just reuse the code that calculates which
+        	// calendar cell contains a given date.
+        	//
+        	numWeeks = dateToCell(daysInMonth).y - dateToCell(1).y + 1;
+
+        	// Remember which holidays fall on which days in this month,
+        	// to save the trouble of having to do it later
+        	fHolidays.setSize(0);
+
+        	for (int h = 0; h < fAllHolidays.length; h++)
+        	{
+            	Date d = fStartOfMonth;
+            	while ( (d = fAllHolidays[h].firstBetween(d, endOfMonth) ) != null)
+            	{
+                	c.setTime(d);
+                	fHolidays.addElement( new HolidayInfo(c.get(Calendar.DATE),
+                                        	fAllHolidays[h],
+                                        	fAllHolidays[h].getDisplayName(fDisplayLocale) ));
+
+                	d.setTime( d.getTime() + 1000 );    // "d++"
+            	}
+        	}
+        	dirty = false;
+    	}
+
+    	static final int INSET = 2;
+
+    	/*
+    	* Convert from the day number within a month (1-based)
+    	* to the cell coordinates on the calendar (0-based)
+    	*/
+    	private void dateToCell(int date, Point pos)
+    	{
+        	int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
+        	if (firstDayInMonth < firstDayOfWeek) {
+            	cell += daysInWeek;
+        	}
+
+        	pos.x = cell % daysInWeek;
+        	pos.y = cell / daysInWeek;
+    	}
+    	private Point dateToCell(int date) {
+        	Point p = new Point(0,0);
+        	dateToCell(date, p);
+        	return p;
+    	}
+
+    	public void paint(Graphics g) {
+
+        	if (dirty) {
+            	calculate();
+        	}
+
+        	Point cellPos = new Point(0,0);     // Temporary variable
+        	Dimension d = this.size();
+
+        	g.setColor(DemoUtility.bgColor);
+        	g.fillRect(0,0,d.width,d.height);
+
+        	// Draw the day names at the top
+        	g.setColor(Color.black);
+        	g.setFont(DemoUtility.labelFont);
+        	FontMetrics fm = g.getFontMetrics();
+        	int labelHeight = fm.getHeight() + INSET * 2;
+
+        	int v = fm.getAscent() + INSET;
+        	for (int i = 0; i < daysInWeek; i++) {
+            	int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek + 1;
+            	String dayName = fSymbols.getWeekdays()[dayNum];
+
+            	int h = (int) (d.width * (i + 0.5)) / daysInWeek;
+            	h -= fm.stringWidth(dayName) / 2;
+
+            	g.drawString(dayName, h, v);
+        	}
+
+        	double cellHeight = (d.height - labelHeight - 1) / numWeeks;
+        	double cellWidth = (double)(d.width - 1) / daysInWeek;
+
+        	// Draw a white background in the part of the calendar
+        	// that displays this month.
+        	// First figure out how much of the first week should be shaded.
+        	{
+            	g.setColor(Color.white);
+            	dateToCell(1, cellPos);
+            	int width = (int)(cellPos.x*cellWidth);  // Width of unshaded area
+
+            	g.fillRect((int)(width), labelHeight ,
+                    	(int)(d.width - width), (int)cellHeight);
+
+            	// All of the intermediate weeks get shaded completely
+            	g.fillRect(0, (int)(labelHeight + cellHeight),
+                        	d.width, (int)(cellHeight * (numWeeks - 2)));
+
+            	// Now figure out the last week.
+            	dateToCell(daysInMonth, cellPos);
+            	width = (int)((cellPos.x+1)*cellWidth);  // Width of shaded area
+
+            	g.fillRect(0, (int)(labelHeight + (numWeeks-1) * cellHeight),
+                        	width, (int)(cellHeight));
+
+        	}
+        	// Draw the X/Y grid lines
+        	g.setColor(Color.black);
+        	for (int i = 0; i <= numWeeks; i++) {
+            	int y = (int)(labelHeight + i * cellHeight);
+            	g.drawLine(0, y, d.width - 1, y);
+        	}
+        	for (int i = 0; i <= daysInWeek; i++) {
+            	int x = (int)(i * cellWidth);
+            	g.drawLine(x, labelHeight, x, d.height - 1);
+        	}
+
+        	// Now loop through all of the days in the month, figure out where
+        	// they go in the grid, and draw the day # for each one
+        	Font numberFont = new Font("Helvetica",Font.PLAIN,12);
+        	Font holidayFont = DemoUtility.creditFont;
+
+        	Calendar c = (Calendar)fCalendar.clone();
+        	c.setTime(fStartOfMonth);
+
+        	for (int i = 1, h = 0; i <= daysInMonth; i++) {
+            	g.setFont(numberFont);
+            	g.setColor(Color.black);
+            	fm = g.getFontMetrics();
+
+            	dateToCell(i, cellPos);
+            	int x = (int)((cellPos.x + 1) * cellWidth);
+            	int y = (int)(cellPos.y * cellHeight + labelHeight);
+
+            	StringBuffer buffer = new StringBuffer();
+            	buffer.append(i);
+            	String dayNum = buffer.toString();
+
+            	x = x - INSET - fm.stringWidth(dayNum);
+            	y = y + fm.getAscent() + INSET;
+
+            	g.drawString(dayNum, x, y);
+
+            	// See if any of the holidays land on this day....
+            	HolidayInfo info = null;
+            	int count = 0;
+
+            	// Coordinates of lower-left corner of cell.
+            	x = (int)((cellPos.x) * cellWidth);
+            	y = (int)((cellPos.y+1) * cellHeight) + labelHeight;
+
+            	while (h < fHolidays.size() &&
+                    	(info = (HolidayInfo)fHolidays.elementAt(h)).date <= i)
+            	{
+                	if (info.date == i) {
+                    	// Draw the holiday here.
+                    	g.setFont(numberFont);
+                    	g.setColor(Color.red);
+
+                    	DemoTextBox box = new DemoTextBox(g, info.name, (int)(cellWidth - INSET));
+                    	box.draw(g, x + INSET, y - INSET - box.getHeight());
+
+                    	y -= (box.getHeight() + INSET);
+                    	count++;
+                	}
+                	h++;
+            	}
+        	}
+    	}
+
+    	// Important state variables
+    	private Locale              fCalendarLocale;    // Whose calendar
+    	private Calendar            fCalendar;          // Calendar for calculations
+
+    	private Locale              fDisplayLocale;     // How to display it
+    	private DateFormatSymbols   fSymbols;           // Symbols for drawing
+
+    	private Date                fStartOfMonth;      // 00:00:00 on first day of month
+
+    	// Cached calculations to make drawing faster.
+    	private transient int minDay;           // Minimum legal day #
+    	private transient int daysInWeek;       // # of days in a week
+    	private transient int firstDayOfWeek;   // First day to display in week
+    	private transient int numWeeks;         // # full or partial weeks in month
+    	private transient int daysInMonth;      // # days in this month
+    	private transient int firstDayInMonth;  // Day of week of first day in month
+
+    	private transient Holiday[] fAllHolidays;
+    	private transient Vector    fHolidays = new Vector(5,5);
+
+    	private transient boolean dirty = true;
+	}
+
+	private static class HolidayInfo {
+    	public HolidayInfo(int date, Holiday holiday, String name) {
+        	this.date = date;
+        	this.holiday = holiday;
+        	this.name = name;
+    	}
+
+    	public Holiday holiday;
+    	public int date;
+    	public String name;
+	}
+}
+
diff --git a/src/com/ibm/demo/rbbi/BreakIteratorRules_en_US_DEMO.java b/src/com/ibm/demo/rbbi/BreakIteratorRules_en_US_DEMO.java
new file mode 100755
index 0000000..db933bc
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/BreakIteratorRules_en_US_DEMO.java
@@ -0,0 +1,105 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbbi/Attic/BreakIteratorRules_en_US_DEMO.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.text.resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ * This resource bundle is included for testing and demonstration purposes only.
+ * It applies the dictionary-based algorithm to English text that has had all the
+ * spaces removed.  Once we have good test cases for Thai, we will replace this
+ * with good resource data (and a good dictionary file) for Thai
+ */
+public class BreakIteratorRules_en_US_DEMO extends ListResourceBundle {
+    public Object[][] getContents() {
+        return contents;
+    }
+
+    static final Object[][] contents = {
+        // names of classes to instantiate for the different kinds of break
+        // iterator.  Notice we're now using DictionaryBasedBreakIterator
+        // for word and line breaking.
+        { "BreakIteratorClasses",
+            new String[] { "RuleBasedBreakIterator",           // character-break iterator class
+                           "DictionaryBasedBreakIterator",     // word-break iterator class
+                           "DictionaryBasedBreakIterator",     // line-break iterator class
+                           "RuleBasedBreakIterator" }          // sentence-break iterator class
+        },
+        
+        // These are the same word-breaking rules as are specified in the default
+        // resource, except that the Latin letters, apostrophe, and hyphen are
+        // specified as dictionary characters
+        { "WordBreakRules",
+            "<ignore>=[:Mn::Me::Cf:];"
+            + "<dictionary>=[a-zA-z\\'\\-];"
+            + "<kanji>=[\u3005\u4e00-\u9fa5\uf900-\ufa2d];"
+            + "<kata>=[\u30a1-\u30fa];"
+            + "<hira>=[\u3041-\u3094];"
+            + "<cjk-diacrit>=[\u3099-\u309c];"
+            + "<let>=[:L:^[<kanji><kata><hira><cjk-diacrit><dictionary>]];"
+            + "<dgt>=[:N:];"
+            + "<mid-word>=[:Pd:\u00ad\u2027\\\"\\\'\\.];"
+            + "<mid-num>=[\\\"\\\'\\,\u066b\\.];"
+            + "<pre-num>=[:Sc:\\#\\.^\u00a2];"
+            + "<post-num>=[\\%\\&\u00a2\u066a\u2030\u2031];"
+            + "<ls>=[\n\u000c\u2028\u2029];"
+            + "<ws>=[:Zs:\t];"
+            + "<word>=(<let><let>*(<mid-word><let><let>*)*|[a-zA-Z][a-z\\'\\-]*);"
+            + "<number>=(<dgt><dgt>*(<mid-num><dgt><dgt>*)*);"
+            + ".;"
+            + "{<word>}(<number><word>)*{<number>{<post-num>}};"
+            + "<pre-num>(<number><word>)*{<number>{<post-num>}};"
+            + "<ws>*{\r}{<ls>};"
+            + "[<kata><cjk-diacrit>]*;"
+            + "[<hira><cjk-diacrit>]*;"
+            + "<kanji>*;" },
+        
+        // These are the same line-breaking rules as are specified in the default
+        // resource, except that the Latin letters, apostrophe, and hyphen are
+        // specified as dictionary characters
+        { "LineBreakRules",
+            "<ignore>=[:Mn::Me::Cf:];"
+            + "<dictionary>=[a-zA-z\\'\\-];"
+            + "<break>=[\u0003\t\n\f\u2028\u2029];"
+            + "<nbsp>=[\u00a0\u2007\u2011\ufeff];"
+            + "<space>=[:Zs::Cc:^[<nbsp><break>\r]];"
+            + "<dash>=[:Pd:\u00ad^<nbsp>];"
+            + "<pre-word>=[:Sc::Ps:^\u00a2];"
+            + "<post-word>=[:Pe:\\!\\%\\.\\,\\:\\;\\?\u00a2\u00b0\u066a\u2030-\u2034\u2103"
+                    + "\u2105\u2109\u3001\u3002\u3005\u3041\u3043\u3045\u3047\u3049\u3063"
+                    + "\u3083\u3085\u3087\u308e\u3099-\u309e\u30a1\u30a3\u30a5\u30a7\u30a9"
+                    + "\u30c3\u30e3\u30e5\u30e7\u30ee\u30f5\u30f6\u30fc-\u30fe\uff01\uff0e"
+                    + "\uff1f];"
+            + "<kanji>=[\u4e00-\u9fa5\uf900-\ufa2d\u3041-\u3094\u30a1-\u30fa^[<post-word><ignore>]];"
+            + "<digit>=[:Nd::No:];"
+            + "<mid-num>=[\\.\\,];"
+            + "<char>=[^[<break><space><dash><kanji><nbsp><ignore><dictionary>\r]];"
+            + "<number>=([<pre-word><dash>]*<digit><digit>*(<mid-num><digit><digit>*)*);"
+            + "<word-core>=(<char>*|<kanji>|<number>|[a-zA-Z][a-z\\'\\-]*);"
+            + "<word-suffix>=((<dash><dash>*|<post-word>*)<space>*);"
+            + "<word>=(<pre-word>*<word-core><word-suffix>);"
+            + "<word>(<nbsp><nbsp>*<word>)*{\r}{<break>};" },
+            
+        // these two resources specify the pathnames of the dictionary files to
+        // use for word breaking and line breaking.  Both currently refer to 
+        // a file called english.dict placed in com\ibm\text\resources
+        // somewhere in the class path.  It's important to note that
+        // english.dict was created for testing purposes only, and doesn't
+        // come anywhere close to being an exhaustive dictionary of English
+        // words (basically, it contains all the words in the Declaration of
+        // Independence, and the Revised Standard Version of the book of Genesis,
+        // plus a few other words thrown in to show more interesting cases).
+        { "WordBreakDictionary", "com\\ibm\\text\\resources\\english.dict" },
+        { "LineBreakDictionary", "com\\ibm\\text\\resources\\english.dict" }
+    };
+}
diff --git a/src/com/ibm/demo/rbbi/DBBIDemo.java b/src/com/ibm/demo/rbbi/DBBIDemo.java
new file mode 100755
index 0000000..83af71a
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/DBBIDemo.java
@@ -0,0 +1,458 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbbi/Attic/DBBIDemo.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.rbbi;
+
+import com.ibm.demo.*;
+import java.applet.Applet;
+import java.awt.*;
+import javax.swing.JTextArea;
+import javax.swing.JScrollPane;
+import javax.swing.BorderFactory;
+import java.util.*;
+
+import com.ibm.text.BreakIterator;
+
+public class DBBIDemo extends DemoApplet
+{
+    public static void main(String argv[]) {
+        Locale.setDefault(new Locale("en", "US", "DEMO"));
+		new DBBIDemo().showDemo();
+    }
+
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new DBBIFrame(applet);
+    }
+}
+
+
+
+class DBBIFrame extends Frame
+{
+    private static final String creditString =
+        "v1.1a9, Demo";
+
+    private static final int FIELD_COLUMNS = 45;
+    private static final Font choiceFont = null;
+    private static final boolean DEBUG = false;
+    private DemoApplet applet;
+
+    final String right = "-->";
+    final String left = "<--";
+
+    private BreakIterator enum;
+
+JTextArea text;
+//    TextArea text;
+    Choice bound;
+
+    public DBBIFrame(DemoApplet applet)
+    {
+        this.applet = applet;
+        init();
+        start();
+    }
+
+
+
+    public void run()
+    {
+        /*
+        while (true) {
+            try {
+                checkChange();
+                Thread.sleep(250);
+            }
+            catch (InterruptedException e) {
+            }
+            catch (Exception e) {
+            }
+            catch (Throwable e) {
+            }
+        }
+        */
+    }
+
+    int s, e;
+    int ts, te;
+
+    public void checkChange()
+    {
+//        System.out.println("checkChange...");
+        if ((text.getSelectionStart() & 0x7FFF) != ts ||
+            (text.getSelectionEnd() & 0x7FFF) != te) {
+
+            int tempS = text.getSelectionStart() & 0x7FFF;
+            int tempE = text.getSelectionEnd() & 0x7FFF;
+
+//          System.out.println(">");
+//          select(0, 0);
+//          select(tempS, tempE);
+            //select(tempS - (ts - s), tempE - (te - e));
+//          System.out.println("<");
+
+
+//          if (s != ts || e != te) System.out.println("     s("+s+") ts("+ts+") e("+e+") te("+te+")");
+//          if (tempS != ts || tempE != te) System.out.println(">s("+s+") tempS("+tempS+") e("+e+") tempE("+tempE+")");
+//          select(s - (ts - s), e - (te - e));
+//          if (tempS != ts || tempE != te) System.out.println("s("+s+") tempS("+tempS+") e("+e+") tempE("+tempE+")");
+
+//          System.out.println("lkdslksj");
+        }
+    }
+
+    public void select(int sIn, int eIn)
+    {
+        s = sIn;
+        e = eIn;
+        text.select(s, e);
+        ts = text.getSelectionStart() & 0x7FFF;
+        te = text.getSelectionEnd() & 0x7FFF;
+//        if (s != ts || e != te) {
+//            System.out.println(">s("+s+") ts("+ts+") e("+e+") te("+te+")");
+//            System.out.println("   "+(ts-s)+","+(te-e));
+//        }
+    }
+
+    public int getSelectionStart()
+    {
+        checkChange();
+//      return s;
+        return text.getSelectionStart() & 0x7FFF;
+    }
+
+
+    public int getSelectionEnd()
+    {
+        checkChange();
+//      return e;
+        return text.getSelectionEnd() & 0x7FFF;
+    }
+
+    public final synchronized void selectRange(int s, int e)
+    {
+        try {
+            //if (getSelectionStart() != s || getSelectionEnd() != e) {
+                //text.select(s, e);
+                select(s,e);
+            //}
+//          if (getSelectionStart() != s || getSelectionEnd() != e) {
+//              System.out.println("AGH! select("+s+","+e+") -> ("+
+//              getSelectionStart()+","+getSelectionEnd()+")");
+//              text.select(s - (getSelectionStart() - s), e - (getSelectionEnd() - e));
+//          }
+        } catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+
+
+    public void init()
+    {
+        buildGUI();
+    }
+
+
+    public void start()
+    {
+    }
+
+
+    void addWithFont(Container container, Component foo, Font font) {
+        if (font != null)
+            foo.setFont(font);
+        container.add(foo);
+    }
+
+
+
+   public void buildGUI()
+    {
+        setBackground(DemoUtility.bgColor);
+        setLayout(new BorderLayout());
+
+       Panel topPanel = new Panel();
+
+            Label titleLabel =
+                new Label("Text Boundary Demo", Label.CENTER);
+            titleLabel.setFont(DemoUtility.titleFont);
+            topPanel.add(titleLabel);
+
+            //Label demo=new Label(creditString, Label.CENTER);
+            //demo.setFont(DemoUtility.creditFont);
+            //topPanel.add(demo);
+
+            Panel choicePanel = new Panel();
+
+            Label demo1=new Label("Boundaries", Label.LEFT);
+            demo1.setFont(DemoUtility.labelFont);
+            choicePanel.add(demo1);
+
+            bound = new Choice();
+                bound.setBackground(DemoUtility.choiceColor);
+            bound.addItem("Sentence");
+            bound.addItem("Line Break");
+            bound.addItem("Word");
+            bound.addItem("Char");
+            if (choiceFont != null)
+                bound.setFont(choiceFont);
+
+            choicePanel.add(bound);
+            topPanel.add(choicePanel);
+
+            DemoUtility.fixGrid(topPanel,1);
+
+
+        add("North", topPanel);
+
+
+            int ROWS = 15;
+            int COLUMNS = 50;
+//            text = new TextArea(getInitialText(), ROWS, COLUMNS);
+text = new JTextArea(getInitialText(), ROWS, COLUMNS);
+text.setLineWrap(true);
+text.setWrapStyleWord(true);
+            text.setEditable(true);
+            text.selectAll();
+            text.setFont(DemoUtility.editFont);
+            text.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+
+        add("Center", new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+                        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
+
+        Panel copyrightPanel = new Panel();
+        addWithFont (copyrightPanel,
+            new Label(DemoUtility.copyright1, Label.LEFT),DemoUtility.creditFont);
+        addWithFont (copyrightPanel,
+            new Label(DemoUtility.copyright2, Label.LEFT),DemoUtility.creditFont);
+        DemoUtility.fixGrid(copyrightPanel,1);
+        add("South", copyrightPanel);
+
+        //layout();
+        handleEnumChanged();
+
+        // (new Thread(this)).start();
+    }
+
+
+
+    public String getInitialText()
+    {
+        return
+"When,inthecourseofhumanevents,itbecomesnecessaryforonepeopletodissolvethepoliticalbondswhichhave"
++ "connectedthemwithanother,andtoassumeamongthepowersoftheearth,theseparateandequalstationtowhichthelaws"
++ "ofnatureandofnature'sGodentitlethem,adecentrespecttotheopinionsofmankindrequiresthattheyshoulddeclarethe"
++ "causeswhichimpelthemtotheseparation.\n"
++ "Weholdthesetruthstobeself-evident,thatallmenarecreatedequal,thattheyareendowedbytheirCreatorwithcertain"
++ "unalienablerights,thatamongthesearelife,libertyandthepursuitofhappiness.Thattosecuretheserights,governmentsare"
++ "institutedamongmen,derivingtheirjustpowersfromtheconsentofthegoverned.Thatwheneveranyformofgovernment"
++ "becomesdestructivetotheseends,itistherightofthepeopletoalterortoabolishit,andtoinstitutenewgovernment,laying"
++ "itsfoundationonsuchprinciplesandorganizingitspowersinsuchform,astothemshallseemmostlikelytoeffecttheirsafety"
++ "andhappiness.Prudence,indeed,willdictatethatgovernmentslongestablishedshouldnotbechangedforlightandtransient"
++ "causes;andaccordinglyallexperiencehathshownthatmankindaremoredisposedtosuffer,whileevilsaresufferable,than"
++ "torightthemselvesbyabolishingtheformstowhichtheyareaccustomed.Butwhenalongtrainofabusesandusurpations,"
++ "pursuinginvariablythesameobjectevincesadesigntoreducethemunderabsolutedespotism,itistheirright,itistheirduty,"
++ "tothrowoffsuchgovernment,andtoprovidenewguardsfortheirfuturesecurity.--Suchhasbeenthepatientsufferanceof"
++ "thesecolonies;andsuchisnowthenecessitywhichconstrainsthemtoaltertheirformersystemsofgovernment.Thehistory"
++ "ofthepresentKingofGreatBritainisahistoryofrepeatedinjuriesandusurpations,allhavingindirectobjectthe"
++ "establishmentofanabsolutetyrannyoverthesestates.Toprovethis,letfactsbesubmittedtoacandidworld.\n"
++ "Hehasrefusedhisassenttolaws,themostwholesomeandnecessaryforthepublicgood.\n"
++ "Hehasforbiddenhisgovernorstopasslawsofimmediateandpressingimportance,unlesssuspendedintheiroperationtill"
++ "hisassentshouldbeobtained;andwhensosuspended,hehasutterlyneglectedtoattendtothem.\n"
++ "Hehasrefusedtopassotherlawsfortheaccommodationoflargedistrictsofpeople,unlessthosepeoplewouldrelinquish"
++ "therightofrepresentationinthelegislature,arightinestimabletothemandformidabletotyrantsonly.\n"
++ "Hehascalledtogetherlegislativebodiesatplacesunusual,uncomfortable,anddistantfromthedepositoryoftheirpublic"
++ "records,forthesolepurposeoffatiguingthemintocompliancewithhismeasures.\n"
++ "Hehasdissolvedrepresentativehousesrepeatedly,foropposingwithmanlyfirmnesshisinvasionsontherightsofthepeople.\n"
++ "Hehasrefusedforalongtime,aftersuchdissolutions,tocauseotherstobeelected;wherebythelegislativepowers,"
++ "incapableofannihilation,havereturnedtothepeopleatlargefortheirexercise;thestateremaininginthemeantimeexposed"
++ "toallthedangersofinvasionfromwithout,andconvulsionswithin.\n"
++ "Hehasendeavoredtopreventthepopulationofthesestates;forthatpurposeobstructingthelawsfornaturalizationof"
++ "foreigners;refusingtopassotherstoencouragetheirmigrationhither,andraisingtheconditionsofnewappropriationsof"
++ "lands.\n"
++ "Hehasobstructedtheadministrationofjustice,byrefusinghisassenttolawsforestablishingjudiciarypowers.\n"
++ "Hehasmadejudgesdependentonhiswillalone,forthetenureoftheiroffices,andtheamountandpaymentoftheirsalaries.\n"
++ "Hehaserectedamultitudeofnewoffices,andsenthitherswarmsofofficerstoharassourpeople,andeatouttheir"
++ "substance.\n"
++ "Hehaskeptamongus,intimesofpeace,standingarmieswithouttheconsentofourlegislature.\n"
++ "Hehasaffectedtorenderthemilitaryindependentofandsuperiortocivilpower.\n"
++ "Hehascombinedwithotherstosubjectustoajurisdictionforeigntoourconstitution,andunacknowledgedbyourlaws;"
++ "givinghisassenttotheiractsofpretendedlegislation:\n"
++ "Forquarteringlargebodiesofarmedtroopsamongus:\n"
++ "Forprotectingthem,bymocktrial,frompunishmentforanymurderswhichtheyshouldcommitontheinhabitantsofthese"
++ "states:\n"
++ "Forcuttingoffourtradewithallpartsoftheworld:\n"
++ "Forimposingtaxesonuswithoutourconsent:\n"
++ "Fordeprivingusinmanycases,ofthebenefitsoftrialbyjury:\n"
++ "Fortransportingusbeyondseastobetriedforpretendedoffenses:\n"
++ "ForabolishingthefreesystemofEnglishlawsinaneighboringprovince,establishingthereinanarbitrarygovernment,and"
++ "enlargingitsboundariessoastorenderitatonceanexampleandfitinstrumentforintroducingthesameabsoluteruleinthese"
++ "colonies:\n"
++ "Fortakingawayourcharters,abolishingourmostvaluablelaws,andalteringfundamentallytheformsofourgovernments:\n"
++ "Forsuspendingourownlegislatures,anddeclaringthemselvesinvestedwithpowertolegislateforusinallcaseswhatsoever.\n"
++ "Hehasabdicatedgovernmenthere,bydeclaringusoutofhisprotectionandwagingwaragainstus.\n"
++ "Hehasplunderedourseas,ravagedourcoasts,burnedourtowns,anddestroyedthelivesofourpeople.\n"
++ "Heisatthistimetransportinglargearmiesofforeignmercenariestocompletetheworksofdeath,desolationandtyranny,"
++ "alreadybegunwithcircumstancesofcrueltyandperfidyscarcelyparalleledinthemostbarbarousages,andtotalyunworth"
++ "theheadofacivilizednation.\n"
++ "Hehasconstrainedourfellowcitizenstakencaptiveonthehighseastobeararmsagainsttheircountry,tobecomethe"
++ "executionersoftheirfriendsandbrethren,ortofallthemselvesbytheirhands.\n"
++ "Hehasexciteddomesticinsurrectionsamongstus,andhasendeavoredtobringontheinhabitantsofourfrontiers,the"
++ "mercilessIndiansavages,whoseknownruleofwarfare,isundistinguisheddestructionofallages,sexesandconditions.\n"
++ "Ineverystageoftheseoppressionswehavepetitionedforredressinthemosthumbleterms:ourrepeatedpetitionshave"
++ "beenansweredonlybyrepeatedinjury.Aprince,whosecharacteristhusmarkedbyeveryactwhichmaydefineatyrant,is"
++ "unfittobetherulerofafreepeople.\n"
++ "NorhavewebeenwantinginattentiontoourBritishbrethren.Wehavewarnedthemfromtimetotimeofattemptsbytheir"
++ "legislaturetoextendanunwarrantablejurisdictionoverus.Wehaveremindedthemofthecircumstancesofouremigration"
++ "andsettlementhere.Wehaveappealedtotheirnativejusticeandmagnanimity,andwehaveconjuredthembythetiesofour"
++ "commonkindredtodisavowtheseusurpations,which,wouldinevitablyinterruptourconnectionsandcorrespondence.We"
++ "must,therefore,acquiesceinthenecessity,whichdenouncesourseparation,andholdthem,asweholdtherestofmankind,"
++ "enemiesinwar,inpeacefriends.\n"
++ "We,therefore,therepresentativesoftheUnitedStatesofAmerica,inGeneralCongress,assembled,appealingtothe"
++ "SupremeJudgeoftheworldfortherectitudeofourintentions,do,inthename,andbytheauthorityofthegoodpeopleof"
++ "thesecolonies,solemnlypublishanddeclare,thattheseunitedcoloniesare,andofrightoughttobefreeandindependent"
++ "states;thattheyareabsolvedfromallallegiancetotheBritishCrown,andthatallpoliticalconnectionbetweenthemandthe"
++ "stateofGreatBritain,isandoughttobetotallydissolved;andthatasfreeandindependentstates,theyhavefullpowerto"
++ "leveywar,concludepeace,contractalliances,establishcommerce,andtodoallotheractsandthingswhichindependent"
++ "statesmayofrightdo.Andforthesupportofthisdeclaration,withafirmrelianceontheprotectionofDivineProvidence,we"
++ "mutuallypledgetoeachotherourlives,ourfortunesandoursacredhonor.\n";
+    }
+
+
+    public void handleEnumChanged()
+    {
+        String s = bound.getSelectedItem();
+        if (s.equals("Char")) {
+            errorText("getCharacterInstance");
+            enum = BreakIterator.getCharacterInstance();
+        }
+        else if (s.equals("Word")) {
+            errorText("tWordBreak");
+            enum = BreakIterator.getWordInstance();
+        }
+        else if (s.equals("Line Break")) {
+            errorText("getLineInstance");
+            enum = BreakIterator.getLineInstance();
+        }
+        else /* if (s.equals("Sentence")) */ {
+            errorText("getSentenceInstance");
+            enum = BreakIterator.getSentenceInstance();
+        }
+        enum.setText(text.getText());
+        selectRange(0, 0);
+        //text.select(0,0);
+    }
+
+    public void handleForward()
+    {
+        try {
+//          System.out.println("entering handleForward");
+            enum.setText(text.getText());
+            int oldStart = getSelectionStart();
+            int oldEnd = getSelectionEnd();
+
+//          System.out.println("handleForward: oldStart=" + oldStart + ", oldEnd=" + oldEnd);
+
+            if (oldEnd < 1) {
+                selectRange(0, enum.following(0));
+            }
+            else {
+                int s = enum.following(oldEnd-1);
+                int e = enum.next();
+                if (e == -1) {
+                    e = s;
+                }
+                selectRange(s, e);
+            }
+            //text.select(s, e);
+            errorText("<" + oldStart + "," + oldEnd + "> -> <" +
+                s + "," + e + ">");
+        }
+        catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+    public void handleBackward()
+    {
+        try {
+            enum.setText(text.getText());
+            int oldStart = getSelectionStart();
+            int oldEnd = getSelectionEnd();
+            if (oldStart < 1) {
+                selectRange(0, 0);
+            }
+            else {
+                int e = enum.following(oldStart-1);
+                int s = enum.previous();
+                selectRange(s, e);
+            }
+            //text.select(s, e);
+            errorText("<" + oldStart + "," + oldEnd + "> -> <" + s + "," + e + ">");
+        }
+        catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+    public boolean action(Event evt, Object obj)
+    {
+
+        if(evt.target instanceof Button && left.equals(obj))
+        {
+            handleBackward();
+            return true;
+        }
+        else if(evt.target instanceof Button && right.equals(obj))
+        {
+            handleForward();
+            return true;
+        }
+        else if(evt.target instanceof Choice)
+        {
+            handleEnumChanged();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean handleEvent(Event evt)
+    {
+        if (evt.id == Event.KEY_PRESS || evt.id == Event.KEY_ACTION) {
+            if (evt.key == Event.RIGHT || (evt.key == 0x0E && evt.controlDown())) {
+                handleForward();
+                return true;
+            }
+            else if (evt.key == Event.LEFT || (evt.key == 0x10 && evt.controlDown())) {
+                handleBackward();
+                return true;
+            }
+        }
+        else
+        if (evt.id == Event.WINDOW_DESTROY && evt.target == this) {
+            this.hide();
+            this.dispose();
+                if (applet != null) {
+                  applet.demoClosed();
+               } else System.exit(0);
+            return true;
+        }
+        return super.handleEvent(evt);
+    }
+
+    public void errorText(String s)
+    {
+       if (DEBUG)
+           System.out.println(s);
+    }
+}
diff --git a/src/com/ibm/demo/rbbi/TextBoundDemo.java b/src/com/ibm/demo/rbbi/TextBoundDemo.java
new file mode 100755
index 0000000..1609019
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/TextBoundDemo.java
@@ -0,0 +1,424 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbbi/Attic/TextBoundDemo.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.rbbi;
+
+import com.ibm.demo.*;
+import java.applet.Applet;
+import java.awt.*;
+import javax.swing.JTextArea;
+import javax.swing.JScrollPane;
+import javax.swing.BorderFactory;
+import java.util.*;
+
+import com.ibm.text.BreakIterator;
+
+public class TextBoundDemo extends DemoApplet
+{
+    public static void main(String argv[]) {
+        new TextBoundDemo().showDemo();
+    }
+
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new TextBoundFrame(applet);
+    }
+}
+
+
+
+class TextBoundFrame extends Frame
+{
+    private static final String creditString =
+        "v1.1a9, Demo";
+
+    private static final int FIELD_COLUMNS = 45;
+    private static final Font choiceFont = null;
+    private static final boolean DEBUG = false;
+    private DemoApplet applet;
+
+    final String right = "-->";
+    final String left = "<--";
+
+    private BreakIterator enum;
+
+JTextArea text;
+//    TextArea text;
+    Choice bound;
+
+    public TextBoundFrame(DemoApplet applet)
+    {
+        this.applet = applet;
+        init();
+        start();
+    }
+
+
+
+    public void run()
+    {
+        /*
+        while (true) {
+            try {
+                checkChange();
+                Thread.sleep(250);
+            }
+            catch (InterruptedException e) {
+            }
+            catch (Exception e) {
+            }
+            catch (Throwable e) {
+            }
+        }
+        */
+    }
+
+    int s, e;
+    int ts, te;
+
+    public void checkChange()
+    {
+//        System.out.println("checkChange...");
+        if ((text.getSelectionStart() & 0x7FFF) != ts ||
+            (text.getSelectionEnd() & 0x7FFF) != te) {
+
+            int tempS = text.getSelectionStart() & 0x7FFF;
+            int tempE = text.getSelectionEnd() & 0x7FFF;
+
+//          System.out.println(">");
+//          select(0, 0);
+//          select(tempS, tempE);
+            //select(tempS - (ts - s), tempE - (te - e));
+//          System.out.println("<");
+
+
+//          if (s != ts || e != te) System.out.println("     s("+s+") ts("+ts+") e("+e+") te("+te+")");
+//          if (tempS != ts || tempE != te) System.out.println(">s("+s+") tempS("+tempS+") e("+e+") tempE("+tempE+")");
+//          select(s - (ts - s), e - (te - e));
+//          if (tempS != ts || tempE != te) System.out.println("s("+s+") tempS("+tempS+") e("+e+") tempE("+tempE+")");
+
+//          System.out.println("lkdslksj");
+        }
+    }
+
+    public void select(int sIn, int eIn)
+    {
+        s = sIn;
+        e = eIn;
+        text.select(s, e);
+        ts = text.getSelectionStart() & 0x7FFF;
+        te = text.getSelectionEnd() & 0x7FFF;
+//        if (s != ts || e != te) {
+//            System.out.println(">s("+s+") ts("+ts+") e("+e+") te("+te+")");
+//            System.out.println("   "+(ts-s)+","+(te-e));
+//        }
+    }
+
+    public int getSelectionStart()
+    {
+        checkChange();
+//      return s;
+        return text.getSelectionStart() & 0x7FFF;
+    }
+
+
+    public int getSelectionEnd()
+    {
+        checkChange();
+//      return e;
+        return text.getSelectionEnd() & 0x7FFF;
+    }
+
+    public final synchronized void selectRange(int s, int e)
+    {
+        try {
+            //if (getSelectionStart() != s || getSelectionEnd() != e) {
+                //text.select(s, e);
+                select(s,e);
+            //}
+//          if (getSelectionStart() != s || getSelectionEnd() != e) {
+//              System.out.println("AGH! select("+s+","+e+") -> ("+
+//              getSelectionStart()+","+getSelectionEnd()+")");
+//              text.select(s - (getSelectionStart() - s), e - (getSelectionEnd() - e));
+//          }
+        } catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+
+
+    public void init()
+    {
+        buildGUI();
+    }
+
+
+    public void start()
+    {
+    }
+
+
+    void addWithFont(Container container, Component foo, Font font) {
+        if (font != null)
+            foo.setFont(font);
+        container.add(foo);
+    }
+
+
+
+   public void buildGUI()
+    {
+        setBackground(DemoUtility.bgColor);
+        setLayout(new BorderLayout());
+
+       Panel topPanel = new Panel();
+
+            Label titleLabel =
+                new Label("Text Boundary Demo", Label.CENTER);
+            titleLabel.setFont(DemoUtility.titleFont);
+            topPanel.add(titleLabel);
+
+            //Label demo=new Label(creditString, Label.CENTER);
+            //demo.setFont(DemoUtility.creditFont);
+            //topPanel.add(demo);
+
+            Panel choicePanel = new Panel();
+
+            Label demo1=new Label("Boundaries", Label.LEFT);
+            demo1.setFont(DemoUtility.labelFont);
+            choicePanel.add(demo1);
+
+            bound = new Choice();
+                bound.setBackground(DemoUtility.choiceColor);
+            bound.addItem("Sentence");
+            bound.addItem("Line Break");
+            bound.addItem("Word");
+            bound.addItem("Char");
+            if (choiceFont != null)
+                bound.setFont(choiceFont);
+
+            choicePanel.add(bound);
+            topPanel.add(choicePanel);
+
+            DemoUtility.fixGrid(topPanel,1);
+
+
+        add("North", topPanel);
+
+
+            int ROWS = 15;
+            int COLUMNS = 50;
+//            text = new TextArea(getInitialText(), ROWS, COLUMNS);
+text = new JTextArea(getInitialText(), ROWS, COLUMNS);
+text.setLineWrap(true);
+text.setWrapStyleWord(true);
+            text.setEditable(true);
+            text.selectAll();
+            text.setFont(DemoUtility.editFont);
+            text.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+
+        add("Center", new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+                        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
+
+        Panel copyrightPanel = new Panel();
+        addWithFont (copyrightPanel,
+            new Label(DemoUtility.copyright1, Label.LEFT),DemoUtility.creditFont);
+        addWithFont (copyrightPanel,
+            new Label(DemoUtility.copyright2, Label.LEFT),DemoUtility.creditFont);
+        DemoUtility.fixGrid(copyrightPanel,1);
+        add("South", copyrightPanel);
+
+        //layout();
+        handleEnumChanged();
+
+        // (new Thread(this)).start();
+    }
+
+
+
+    public String getInitialText()
+    {
+        return
+    /*
+            "\"This is a sentence.\" This is not.\" \"because. And go. " +
+            "This is a simple 012.566,5 sample sentence. \n"+
+            "It does not have to make any sense as you can see. \n"+
+            "Nel mezzo del cammin di nostra vita, mi ritrovai in "+
+                "una selva oscura. \n"+
+            "Che la dritta via aveo smarrita. \n"+
+            "He said, that I said, that you said!! \n"+
+            "Don't rock the boat.\n\n"+
+            "Because I am the daddy, that is why. \n"+
+            "Not on my time (el timo.)! \n"+
+            "Tab\tTab\rTab\tWow."+
+            "So what!!\n\n"+
+            "Is this a question???  " +
+            "I wonder...Hmm.\n" +
+            "Harris thumbed down several, including \"Away We Go\" "+
+                "(which became the huge success Oklahoma!). \n"+
+            "One species, B. anthracis, is highly virulent.\n"+
+            "Wolf said about Sounder: \"Beautifully thought-out and "+
+                "directed.\"\n"+
+            "Have you ever said, \"This is where I shall live\"? \n"+
+            "He 1000,233,456.000 answered, \"You may not!\" \n"+
+            "Another popular saying is: \"How do you do?\". \n"+
+            "What is the proper use of the abbreviation pp.? \n"+
+            "Yes, I am 1,23.322% definatelly 12\" tall!!";
+    */
+            "(\"This is a complete sentence.\") This is (\"not.\") also.  "
+            +"An abbreviation in the middle, etc. and one at the end, etc. "+
+                "This "
+            +"is a simple sample 012.566,5 sentence. It doesn't "
+            +"have to make any sense, as you can see. Nel mezzo del  c"
+            +"ammin di nostra vita, mi ritrovai in una selva oscura. Che "
+            +"la dritta via aveo smarrita. Not on my time (el timo.)! And "
+            +"tabulated columns: \tCol1\tCol2\t3,456%.\t "
+            +"Is this a question???  I wonder... Hmm. Harris thumbed "
+            +"down several, including \"Away We Go\" (which became the  "
+            +"huge success Oklahoma!). One species, B. anthracis, is  "
+            +"highly virulent. Wolf said about Sounder: \"Beautifully  "
+            +"thought-out and directed.\" Have you ever said, \"This is "+
+                "where I "
+            +"shall live\"? He said 1000,233,456.000 and answered, \"You "+
+                "may not!\"  "
+            +"Another popular saying is: \"How do you do?\". What is the  "
+            +"proper use of the abbreviation pp.? Yes, I am 12\' 3\" tall!!";
+    }
+
+
+    public void handleEnumChanged()
+    {
+        String s = bound.getSelectedItem();
+        if (s.equals("Char")) {
+            errorText("getCharacterInstance");
+            enum = BreakIterator.getCharacterInstance();
+        }
+        else if (s.equals("Word")) {
+            errorText("tWordBreak");
+            enum = BreakIterator.getWordInstance();
+        }
+        else if (s.equals("Line Break")) {
+            errorText("getLineInstance");
+            enum = BreakIterator.getLineInstance();
+        }
+        else /* if (s.equals("Sentence")) */ {
+            errorText("getSentenceInstance");
+            enum = BreakIterator.getSentenceInstance();
+        }
+        enum.setText(text.getText());
+        selectRange(0, 0);
+        //text.select(0,0);
+    }
+
+    public void handleForward()
+    {
+        try {
+//          System.out.println("entering handleForward");
+            enum.setText(text.getText());
+            int oldStart = getSelectionStart();
+            int oldEnd = getSelectionEnd();
+
+//          System.out.println("handleForward: oldStart=" + oldStart + ", oldEnd=" + oldEnd);
+
+            if (oldEnd < 1) {
+                selectRange(0, enum.following(0));
+            }
+            else {
+                int s = enum.following(oldEnd-1);
+                int e = enum.next();
+                if (e == -1) {
+                    e = s;
+                }
+                selectRange(s, e);
+            }
+            //text.select(s, e);
+            errorText("<" + oldStart + "," + oldEnd + "> -> <" +
+                s + "," + e + ">");
+        }
+        catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+    public void handleBackward()
+    {
+        try {
+            enum.setText(text.getText());
+            int oldStart = getSelectionStart();
+            int oldEnd = getSelectionEnd();
+            if (oldStart < 1) {
+                selectRange(0, 0);
+            }
+            else {
+                int e = enum.following(oldStart-1);
+                int s = enum.previous();
+                selectRange(s, e);
+            }
+            //text.select(s, e);
+            errorText("<" + oldStart + "," + oldEnd + "> -> <" + s + "," + e + ">");
+        }
+        catch (Exception exp) {
+            errorText(exp.toString());
+        }
+    }
+
+    public boolean action(Event evt, Object obj)
+    {
+
+        if(evt.target instanceof Button && left.equals(obj))
+        {
+            handleBackward();
+            return true;
+        }
+        else if(evt.target instanceof Button && right.equals(obj))
+        {
+            handleForward();
+            return true;
+        }
+        else if(evt.target instanceof Choice)
+        {
+            handleEnumChanged();
+            return true;
+        }
+        return false;
+    }
+
+    public boolean handleEvent(Event evt)
+    {
+        if (evt.id == Event.KEY_PRESS || evt.id == Event.KEY_ACTION) {
+            if (evt.key == Event.RIGHT || (evt.key == 0x0E && evt.controlDown())) {
+                handleForward();
+                return true;
+            }
+            else if (evt.key == Event.LEFT || (evt.key == 0x10 && evt.controlDown())) {
+                handleBackward();
+                return true;
+            }
+        }
+        else
+        if (evt.id == Event.WINDOW_DESTROY && evt.target == this) {
+            this.hide();
+            this.dispose();
+                if (applet != null) {
+                  applet.demoClosed();
+               } else System.exit(0);
+            return true;
+        }
+        return super.handleEvent(evt);
+    }
+
+    public void errorText(String s)
+    {
+       if (DEBUG)
+           System.out.println(s);
+    }
+}
diff --git a/src/com/ibm/demo/rbbi/english.dict b/src/com/ibm/demo/rbbi/english.dict
new file mode 100755
index 0000000..860dcbe
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/english.dict
Binary files differ
diff --git a/src/com/ibm/demo/rbnf/RbnfDemo.java b/src/com/ibm/demo/rbnf/RbnfDemo.java
new file mode 100755
index 0000000..e78295a
--- /dev/null
+++ b/src/com/ibm/demo/rbnf/RbnfDemo.java
@@ -0,0 +1,533 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbnf/Attic/RbnfDemo.java,v $ 
+ * $Date: 2000/03/10 03:47:43 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.rbnf;
+
+import com.ibm.demo.*;
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.text.DecimalFormat;
+import java.text.BreakIterator;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import com.ibm.text.RuleBasedNumberFormat;
+
+public class RbnfDemo extends DemoApplet {
+    /**
+     * Puts a copyright in the .class file
+     */
+    private static final String copyrightNotice
+        = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
+
+    /*
+     * code to run the demo as an application
+     */
+    public static void main(String[] argv) {
+    	new RbnfDemo().showDemo();
+    }
+
+    protected Dimension getDefaultFrameSize(DemoApplet applet, Frame f) {
+    	return new Dimension(430,270);
+    }
+
+    protected Frame createDemoFrame(DemoApplet applet) {
+        final Frame window = new Frame("Number Spellout Demo");
+        window.setSize(800, 600);
+        window.setLayout(new BorderLayout());
+
+        Panel mainPanel = new Panel();
+        mainPanel.setLayout(new GridLayout(1,2));
+
+        commentaryField = new TextArea("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
+        commentaryField.setSize(800, 50);
+        commentaryField.setText(RbnfSampleRuleSets.sampleRuleSetCommentary[0]);
+        commentaryField.setEditable(false);
+        commentaryField.setFont(new Font("Helvetica", Font.PLAIN, 14));
+
+        spelloutFormatter = new RuleBasedNumberFormat(RbnfSampleRuleSets.usEnglish, Locale.US);
+        spelloutFormatter.setLenientParseMode(lenientParse);
+        populateRuleSetMenu();
+        numberFormatter = new DecimalFormat("#,##0.##########");
+        parsePosition = new ParsePosition(0);
+        theNumber = 0;
+
+        numberField = new TextField();
+        numberField.setFont(new Font("Serif", Font.PLAIN, 24));
+        textField = new DemoTextFieldHolder();
+        textField.setFont(new Font("Serif", Font.PLAIN, 24));
+        rulesField = new DemoTextFieldHolder();
+        rulesField.setFont(new Font("Serif", Font.PLAIN, 14));
+        lenientParseButton = new Checkbox("Lenient parse", lenientParse);
+
+        numberField.addTextListener(new TextListener() {
+            public void textValueChanged(TextEvent e) {
+                if (!numberFieldHasFocus)
+                    return;
+
+                String fieldText = ((TextComponent)(e.getSource())).getText();
+                parsePosition.setIndex(0);
+                Number temp = numberFormatter.parse(fieldText, parsePosition);
+                if (temp == null || parsePosition.getIndex() == 0) {
+                    theNumber = 0;
+                    textField.setText("PARSE ERROR");
+                }
+                else {
+                    theNumber = temp.doubleValue();
+                    textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
+                }
+            }
+        } );
+
+        numberField.addFocusListener(new FocusAdapter() {
+            public void focusLost(FocusEvent e) {
+                numberFieldHasFocus = false;
+                numberField.setText(numberFormatter.format(theNumber));
+            }
+
+            public void focusGained(FocusEvent e) {
+                numberFieldHasFocus = true;
+                numberField.selectAll();
+            }
+        } );
+
+        textField.addKeyListener(new KeyAdapter() {
+            public void keyTyped(KeyEvent e) {
+                if (e.getKeyChar() == '\t') {
+                    String fieldText = ((TextComponent)(e.getSource())).getText();
+                    parsePosition.setIndex(0);
+                    theNumber = spelloutFormatter.parse(fieldText, parsePosition)
+                                        .doubleValue();
+                    if (parsePosition.getIndex() == 0) {
+                        theNumber = 0;
+                        numberField.setText("PARSE ERROR");
+                        textField.selectAll();
+                    }
+                    else if (parsePosition.getIndex() < fieldText.length()) {
+                        textField.select(parsePosition.getIndex(), fieldText.length());
+                        numberField.setText(numberFormatter.format(theNumber));
+                    }
+                    else {
+                        textField.selectAll();
+                        numberField.setText(numberFormatter.format(theNumber));
+                    }
+                    e.consume();
+                }
+            }
+        } );
+
+        textField.addFocusListener(new FocusAdapter() {
+            public void focusLost(FocusEvent e) {
+                String fieldText = ((TextComponent)(e.getSource())).getText();
+                parsePosition.setIndex(0);
+                theNumber = spelloutFormatter.parse(fieldText, parsePosition)
+                                .doubleValue();
+                if (parsePosition.getIndex() == 0)
+                    numberField.setText("PARSE ERROR");
+                else
+                    numberField.setText(numberFormatter.format(theNumber));
+                textField.setText(textField.getText()); // textField.repaint() didn't work right
+            }
+
+            public void focusGained(FocusEvent e) {
+                textField.selectAll();
+            }
+        } );
+
+        rulesField.addKeyListener(new KeyAdapter() {
+            public void keyTyped(KeyEvent e) {
+                if (e.getKeyChar() == '\t') {
+                    String fieldText = ((TextComponent)(e.getSource())).getText();
+                    if (formatterMenu.getSelectedItem().equals("Custom") || !fieldText.equals(
+                                    RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()])) {
+                        try {
+                            RuleBasedNumberFormat temp = new RuleBasedNumberFormat(fieldText);
+                            temp.setLenientParseMode(lenientParse);
+                            populateRuleSetMenu();
+                            spelloutFormatter = temp;
+                            customRuleSet = fieldText;
+                            formatterMenu.select("Custom");
+                            commentaryField.setText(RbnfSampleRuleSets.
+                                sampleRuleSetCommentary[RbnfSampleRuleSets.
+                                sampleRuleSetCommentary.length - 1]);
+                            redisplay();
+                        }
+                        catch (Exception x) {
+                            textField.setText(x.toString());
+                        }
+                    }
+                    e.consume();
+                }
+            }
+        } );
+
+        rulesField.addFocusListener(new FocusAdapter() {
+            public void focusLost(FocusEvent e) {
+                String fieldText = ((TextComponent)(e.getSource())).getText();
+                if (formatterMenu.getSelectedItem().equals("Custom") || !fieldText.equals(
+                                RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()])) {
+                    try {
+                        RuleBasedNumberFormat temp = new RuleBasedNumberFormat(fieldText);
+                        temp.setLenientParseMode(lenientParse);
+                        populateRuleSetMenu();
+                        spelloutFormatter = temp;
+                        customRuleSet = fieldText;
+                        formatterMenu.select("Custom");
+                        redisplay();
+                    }
+                    catch (Exception x) {
+                        textField.setText(x.toString());
+                    }
+                }
+                rulesField.setText(rulesField.getText()); // rulesField.repaint() didn't work right
+            }
+        } );
+
+        lenientParseButton.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                lenientParse = lenientParseButton.getState();
+                spelloutFormatter.setLenientParseMode(lenientParse);
+            }
+        } );
+
+        numberField.setText(numberFormatter.format(theNumber));
+        numberField.selectAll();
+        textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
+
+        Panel leftPanel = new Panel();
+        leftPanel.setLayout(new BorderLayout());
+        Panel panel = new Panel();
+        panel.setLayout(new BorderLayout());
+        Panel panel1 = new Panel();
+        panel1.setLayout(new GridLayout(3, 1));
+        panel1.add(new Panel());
+        panel1.add(numberField, "Center");
+        panel1.add(lenientParseButton);
+        panel.add(panel1, "Center");
+        Panel panel2 = new Panel();
+        panel2.setLayout(new GridLayout(3, 3));
+        Button button = new Button("+100");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(100);
+            }
+        } );
+        panel2.add(button);
+        button = new Button("+10");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(10);
+            }
+        } );
+        panel2.add(button);
+        button = new Button("+1");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(1);
+            }
+        } );
+        panel2.add(button);
+        button = new Button("<");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                theNumber *= 10;
+                redisplay();
+            }
+        } );
+        panel2.add(button);
+        panel2.add(new Panel());
+        button = new Button(">");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                theNumber /= 10;
+                redisplay();
+            }
+        } );
+        panel2.add(button);
+        button = new Button("-100");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(-100);
+            }
+        } );
+        panel2.add(button);
+        button = new Button("-10");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(-10);
+            }
+        } );
+        panel2.add(button);
+        button = new Button("-1");
+        button.addActionListener( new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                roll(-1);
+            }
+        } );
+        panel2.add(button);
+        panel.add(panel2, "East");
+        leftPanel.add(panel, "North");
+        leftPanel.add(textField, "Center");
+
+        Panel rightPanel = new Panel();
+        rightPanel.setLayout(new BorderLayout());
+        formatterMenu = new Choice();
+        for (int i = 0; i < RbnfSampleRuleSets.sampleRuleSetNames.length; i++)
+            formatterMenu.addItem(RbnfSampleRuleSets.sampleRuleSetNames[i]);
+        formatterMenu.addItem("Custom");
+        formatterMenu.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                Choice source = (Choice)(e.getSource());
+                int item = source.getSelectedIndex();
+                Locale locale = RbnfSampleRuleSets.sampleRuleSetLocales[item];
+
+                commentaryField.setText(RbnfSampleRuleSets.
+                                sampleRuleSetCommentary[item]);
+
+                if (locale != null && (locale.getLanguage().equals("iw")
+                        || locale.getLanguage().equals("ru") || locale.getLanguage().equals("ja")
+                        || locale.getLanguage().equals("el")
+                        || locale.getLanguage().equals("zh"))) {
+                    textField.togglePanes(false);
+                    rulesField.togglePanes(false);
+                }
+                else {
+                    textField.togglePanes(true);
+                    rulesField.togglePanes(true);
+                }
+
+                makeNewSpelloutFormatter();
+                redisplay();
+            }
+        } );
+
+        ruleSetMenu = new Choice();
+        populateRuleSetMenu();
+
+        ruleSetMenu.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                ruleSetName = ruleSetMenu.getSelectedItem();
+                redisplay();
+            }
+        } );
+
+        Panel menuPanel = new Panel();
+        menuPanel.setLayout(new GridLayout(1, 2));
+        menuPanel.add(formatterMenu);
+        menuPanel.add(ruleSetMenu);
+        rightPanel.add(menuPanel, "North");
+
+        rulesField.setText(RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()]);
+        rightPanel.add(rulesField, "Center");
+
+        mainPanel.add(leftPanel);
+        mainPanel.add(rightPanel);
+
+        window.add(mainPanel, "Center");
+        window.add(commentaryField, "South");
+
+        window.doLayout();
+        window.show();
+        return window;
+    }
+
+    void roll(int delta) {
+        theNumber += delta;
+        redisplay();
+    }
+
+    void redisplay() {
+        numberField.setText(numberFormatter.format(theNumber));
+        textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
+    }
+
+    void makeNewSpelloutFormatter() {
+        int item = formatterMenu.getSelectedIndex();
+        String formatterMenuItem = formatterMenu.getSelectedItem();
+
+        if (formatterMenuItem.equals("Custom")) {
+            rulesField.setText(customRuleSet);
+            spelloutFormatter = new RuleBasedNumberFormat(customRuleSet);
+        }
+        else {
+            rulesField.setText(RbnfSampleRuleSets.sampleRuleSets[item]);
+
+            Locale locale = RbnfSampleRuleSets.sampleRuleSetLocales[item];
+            if (locale == null)
+                locale = Locale.getDefault();
+
+            spelloutFormatter = new RuleBasedNumberFormat(RbnfSampleRuleSets.
+                            sampleRuleSets[item], locale);
+        }
+        spelloutFormatter.setLenientParseMode(lenientParse);
+        populateRuleSetMenu();
+    }
+
+    void populateRuleSetMenu() {
+        String[] ruleSetNames = spelloutFormatter.getRuleSetNames();
+
+        if (ruleSetMenu != null) {
+            ruleSetMenu.removeAll();
+            for (int i = 0; i < ruleSetNames.length; i++)
+                ruleSetMenu.addItem(ruleSetNames[i]);
+
+            ruleSetName = ruleSetMenu.getSelectedItem();
+        }
+        else
+            ruleSetName = ruleSetNames[0];
+    }
+
+    private Frame demoWindow = null;
+
+    private TextComponent numberField;
+    private DemoTextFieldHolder textField;
+    private DemoTextFieldHolder rulesField;
+    private TextComponent commentaryField;
+    private Checkbox lenientParseButton;
+
+    private boolean numberFieldHasFocus = true;
+
+    private RuleBasedNumberFormat spelloutFormatter;
+    private DecimalFormat numberFormatter;
+    private ParsePosition parsePosition;
+
+    private boolean lenientParse = true;
+
+    private double theNumber = 0;
+    private boolean canEdit = true;
+
+    private Choice formatterMenu;
+    private Choice ruleSetMenu;
+    private String ruleSetName;
+
+    private String customRuleSet = "NO RULES!";
+}
+
+class DemoTextField extends Component {
+    public DemoTextField() {
+    }
+
+    public void setText(String text) {
+        this.text = text;
+        this.repaint();
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void paint(Graphics g) {
+        Font font = getFont();
+        FontMetrics fm = g.getFontMetrics();
+        g.setFont(font);
+        String text = getText();
+        BreakIterator bi = BreakIterator.getLineInstance();
+        bi.setText(text);
+        int lineHeight = fm.getHeight();
+        int width = getSize().width;
+        int penY = fm.getAscent();
+        int lineStart = 0;
+        int tempLineEnd = bi.first();
+        int lineEnd = 0;
+        int maxLineEnd = 0;
+        totalHeight = 0;
+
+        while (lineStart < text.length()) {
+            maxLineEnd = text.indexOf('\n', lineStart);
+            if (maxLineEnd == -1)
+                maxLineEnd = Integer.MAX_VALUE;
+            while (tempLineEnd != BreakIterator.DONE && fm.stringWidth(text.substring(
+                            lineStart, tempLineEnd)) < width) {
+                lineEnd = tempLineEnd;
+                tempLineEnd = bi.next();
+            }
+            if (lineStart >= lineEnd) {
+                if (tempLineEnd == BreakIterator.DONE)
+                    lineEnd = text.length();
+                else
+                    lineEnd = tempLineEnd;
+            }
+            if (lineEnd > maxLineEnd)
+                lineEnd = maxLineEnd;
+            g.drawString(text.substring(lineStart, lineEnd), 0, penY);
+            penY += lineHeight;
+            totalHeight += lineHeight;
+            lineStart = lineEnd;
+            if (lineStart < text.length() && text.charAt(lineStart) == '\n')
+                ++lineStart;
+        }
+    }
+
+/*
+    public Dimension getPreferredSize() {
+        Dimension size = getParent().getSize();
+        return new Dimension(size.width, totalHeight);
+    }
+*/
+
+    private String text;
+    private int totalHeight;
+}
+
+class DemoTextFieldHolder extends Panel {
+    public DemoTextFieldHolder() {
+        tf1 = new TextArea("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
+        tf2 = new DemoTextField();
+        sp = new ScrollPane();
+
+        setLayout(new CardLayout());
+
+        sp.add(tf2, "TextField1");
+        sp.setVisible(false);
+        add(tf1, "TestField2");
+        add(sp, "ScrollPane");
+    }
+
+    public void addFocusListener(FocusListener l) {
+        tf1.addFocusListener(l);
+    }
+
+    public void addKeyListener(KeyListener l) {
+        tf1.addKeyListener(l);
+    }
+
+    public void setText(String text) {
+        tf1.setText(text);
+        tf2.setText(text);
+    }
+
+    public String getText() {
+        return tf1.getText();
+    }
+
+    public void select(int start, int end) {
+        tf1.select(start, end);
+    }
+
+    public void selectAll() {
+        tf1.selectAll();
+    }
+
+    public void togglePanes(boolean canShowRealTextField) {
+        if (canShowRealTextField != showingRealTextField) {
+            CardLayout layout = (CardLayout)(getLayout());
+            layout.next(this);
+            showingRealTextField = canShowRealTextField;
+        }
+    }
+
+    private TextArea tf1 = null;
+    private DemoTextField tf2 = null;
+    private ScrollPane sp = null;
+    private boolean showingRealTextField = true;
+}
diff --git a/src/com/ibm/demo/rbnf/RbnfSampleRuleSets.java b/src/com/ibm/demo/rbnf/RbnfSampleRuleSets.java
new file mode 100755
index 0000000..2294212
--- /dev/null
+++ b/src/com/ibm/demo/rbnf/RbnfSampleRuleSets.java
@@ -0,0 +1,1949 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbnf/Attic/RbnfSampleRuleSets.java,v $ 
+ * $Date: 2000/03/10 03:47:44 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.rbnf;
+
+import com.ibm.demo.*;
+import java.util.Locale;
+
+/**
+ * A collection of example rule sets for use with RuleBasedNumberFormat.
+ * These examples are intended to serve both as demonstrations of what can
+ * be done with this framework, and as starting points for designing new
+ * rule sets.
+ *
+ * For those that claim to represent number-spellout rules for languages
+ * other than U.S. English, we make no claims of either accuracy or
+ * completeness.  In fact, we know them to be incomplete, and suspect
+ * most have mistakes in them.  If you see something that you know is wrong,
+ * please tell us!
+ *
+ * @author Richard Gillam
+ * @version $Version$ $Date: 2000/03/10 03:47:44 $
+ */
+public class RbnfSampleRuleSets {
+    /**
+     * Puts a copyright in the .class file
+     */
+    private static final String copyrightNotice
+        = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
+
+    //========================================================================
+    // Spellout rules for various languages
+    //
+    // The following RuleBasedNumberFormat descriptions show the rules for
+    // spelling out numeric values in various languages.  As mentioned
+    // before, we cannot vouch for the accuracy or completeness of this
+    // data, although we believe it's pretty close.  Basically, this
+    // represents one day's worth of Web-surfing.  If you can supply the
+    // missing information in any of these rule sets, or if you find errors,
+    // or if you can supply spellout rules for languages that aren't shown
+    // here, we want to hear from you!
+    //========================================================================
+
+    /**
+     * Spellout rules for U.S. English.  This demonstration version of the
+     * U.S. English spellout rules has four variants: 1) %simplified is a
+     * set of rules showing the simple method of spelling out numbers in
+     * English: 289 is formatted as "two hundred eighty-nine".  2) %alt-teens
+     * is the same as %simplified, except that values between 1,000 and 9,999
+     * whose hundreds place isn't zero are formatted in hundreds.  For example,
+     * 1,983 is formatted as "nineteen hundred eighty-three," and 2,183 is
+     * formatted as "twenty-one hundred eighty-three," but 2,083 is still
+     * formatted as "two thousand eighty-three."  3) %ordinal formats the
+     * values as ordinal numbers in English (e.g., 289 is "two hundred eighty-
+     * ninth").  4) %default uses a more complicated algorithm to format
+     * numbers in a more natural way: 289 is formatted as "two hundred AND
+     * eighty-nine" and commas are inserted between the thousands groups for
+     * values above 100,000.
+     */
+    public static final String usEnglish =
+        // This rule set shows the normal simple formatting rules for English
+        "%simplified:\n"
+               // negative number rule.  This rule is used to format negative
+               // numbers.  The result of formatting the number's absolute
+               // value is placed where the >> is.
+        + "    -x: minus >>;\n"
+               // faction rule.  This rule is used for formatting numbers
+               // with fractional parts.  The result of formatting the
+               // number's integral part is substituted for the <<, and
+               // the result of formatting the number's fractional part
+               // (one digit at a time, e.g., 0.123 is "zero point one two
+               // three") replaces the >>.
+        + "    x.x: << point >>;\n"
+               // the rules for the values from 0 to 19 are simply the
+               // words for those numbers
+        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
+        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
+        + "        seventeen; eighteen; nineteen;\n"
+               // beginning at 20, we use the >> to mark the position where
+               // the result of formatting the number's ones digit.  Thus,
+               // we only need a new rule at every multiple of 10.  Text in
+               // backets is omitted if the value being formatted is an
+               // even multiple of 10.
+        + "    20: twenty[->>];\n"
+        + "    30: thirty[->>];\n"
+        + "    40: forty[->>];\n"
+        + "    50: fifty[->>];\n"
+        + "    60: sixty[->>];\n"
+        + "    70: seventy[->>];\n"
+        + "    80: eighty[->>];\n"
+        + "    90: ninety[->>];\n"
+               // beginning at 100, we can use << to mark the position where
+               // the result of formatting the multiple of 100 is to be
+               // inserted.  Notice also that the meaning of >> has shifted:
+               // here, it refers to both the ones place and the tens place.
+               // The meanings of the << and >> tokens depend on the base value
+               // of the rule.  A rule's divisor is (usually) the highest
+               // power of 10 that is less than or equal to the rule's base
+               // value.  The value being formatted is divided by the rule's
+               // divisor, and the integral quotient is used to get the text
+               // for <<, while the remainder is used to produce the text
+               // for >>.  Again, text in brackets is omitted if the value
+               // being formatted is an even multiple of the rule's divisor
+               // (in this case, an even multiple of 100)
+        + "    100: << hundred[ >>];\n"
+               // The rules for the higher numbers work the same way as the
+               // rule for 100: Again, the << and >> tokens depend on the
+               // rule's divisor, which for all these rules is also the rule's
+               // base value.  To group by thousand, we simply don't have any
+               // rules between 1,000 and 1,000,000.
+        + "    1000: << thousand[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000: << trillion[ >>];\n"
+               // overflow rule.  This rule specifies that values of a
+               // quadrillion or more are shown in numerals rather than words.
+               // The == token means to format (with new rules) the value
+               // being formatted by this rule and place the result where
+               // the == is.  The #,##0 inside the == signs is a
+               // DecimalFormat pattern.  It specifies that the value should
+               // be formatted with a DecimalFormat object, and that it
+               // should be formatted with no decimal places, at least one
+               // digit, and a thousands separator.
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+
+        // This rule set formats numbers between 1,000 and 9,999 somewhat
+        // differently: If the hundreds digit is not zero, the first two
+        // digits are treated as a number of hundreds.  For example, 2,197
+        // would come out as "twenty-one hundred ninety-seven."
+        + "%alt-teens:\n"
+               // just use %simplified to format values below 1,000
+        + "    =%simplified=;\n"
+               // values between 1,000 and 9,999 are delegated to %%alt-hundreds
+               // for formatting.  The > after "1000" decreases the exponent
+               // of the rule's radix by one, causing the rule's divisor
+               // to be 100 instead of 1,000.  This causes the first TWO
+               // digits of the number, instead of just the first digit,
+               // to be sent to %%alt-hundreds
+        + "    1000>: <%%alt-hundreds<[ >>];\n"
+               // for values of 10,000 and more, we again just use %simplified
+        + "    10,000: =%simplified=;\n"
+        // This rule set uses some obscure voodoo of the description language
+        // to format the first two digits of a value in the thousands.
+        // The rule at 10 formats the first two digits as a multiple of 1,000
+        // and the rule at 11 formats the first two digits as a multiple of
+        // 100.  This works because of something known as the "rollback rule":
+        // if the rule applicable to the value being formatted has two
+        // substitutions, the value being formatted is an even multiple of
+        // the rule's divisor, and the rule's base value ISN'T an even multiple
+        // if the rule's divisor, then the rule that precedes this one in the
+        // list is used instead.  (The [] notation is implemented internally
+        // using this notation: a rule containing [] is split into two rules,
+        // and the right one is chosen using the rollback rule.) In this case,
+        // it means that if the first two digits are an even multiple of 10,
+        // they're formatted with the 10 rule (containing "thousand"), and if
+        // they're not, they're formatted with the 11 rule (containing
+        // "hundred").  %%empty is a hack to cause the rollback rule to be
+        // invoked: it makes the 11 rule have two substitutions, even though
+        // the second substitution (calling %%empty) doesn't actually do
+        // anything.
+        + "%%alt-hundreds:\n"
+        + "    0: SHOULD NEVER GET HERE!;\n"
+        + "    10: <%simplified< thousand;\n"
+        + "    11: =%simplified= hundred>%%empty>;\n"
+        + "%%empty:\n"
+        + "    0:;"
+
+        // this rule set is the same as %simplified, except that it formats
+        // the value as an ordinal number: 234 is formatted as "two hundred
+        // thirty-fourth".  Notice the calls to ^simplified: we have to
+        // call %simplified to avoid getting "second hundred thirty-fourth."
+        + "%ordinal:\n"
+        + "    zeroth; first; second; third; fourth; fifth; sixth; seventh;\n"
+        + "        eighth; ninth;\n"
+        + "    tenth; eleventh; twelfth; thirteenth; fourteenth;\n"
+        + "        fifteenth; sixteenth; seventeenth; eighteenth;\n"
+        + "        nineteenth;\n"
+        + "    twentieth; twenty->>;\n"
+        + "    30: thirtieth; thirty->>;\n"
+        + "    40: fortieth; forty->>;\n"
+        + "    50: fiftieth; fifty->>;\n"
+        + "    60: sixtieth; sixty->>;\n"
+        + "    70: seventieth; seventy->>;\n"
+        + "    80: eightieth; eighty->>;\n"
+        + "    90: ninetieth; ninety->>;\n"
+        + "    100: <%simplified< hundredth; <%simplified< hundred >>;\n"
+        + "    1000: <%simplified< thousandth; <%simplified< thousand >>;\n"
+        + "    1,000,000: <%simplified< millionth; <%simplified< million >>;\n"
+        + "    1,000,000,000: <%simplified< billionth;\n"
+        + "        <%simplified< billion >>;\n"
+        + "    1,000,000,000,000: <%simplified< trillionth;\n"
+        + "        <%simplified< trillion >>;\n"
+        + "    1,000,000,000,000,000: =#,##0=;"
+
+        // %default is a more elaborate form of %simplified;  It is basically
+        // the same, except that it introduces "and" before the ones digit
+        // when appropriate (basically, between the tens and ones digits) and
+        // separates the thousands groups with commas in values over 100,000.
+        + "%default:\n"
+               // negative-number and fraction rules.  These are the same
+               // as those for %simplified, but ave to be stated here too
+               // because this is an entry point
+        + "    -x: minus >>;\n"
+        + "    x.x: << point >>;\n"
+               // just use %simplified for values below 100
+        + "    =%simplified=;\n"
+               // for values from 100 to 9,999 use %%and to decide whether or
+               // not to interpose the "and"
+        + "    100: << hundred[ >%%and>];\n"
+        + "    1000: << thousand[ >%%and>];\n"
+               // for values of 100,000 and up, use %%commas to interpose the
+               // commas in the right places (and also to interpose the "and")
+        + "    100,000>>: << thousand[>%%commas>];\n"
+        + "    1,000,000: << million[>%%commas>];\n"
+        + "    1,000,000,000: << billion[>%%commas>];\n"
+        + "    1,000,000,000,000: << trillion[>%%commas>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        // if the value passed to this rule set is greater than 100, don't
+        // add the "and"; if it's less than 100, add "and" before the last
+        // digits
+        + "%%and:\n"
+        + "    and =%default=;\n"
+        + "    100: =%default=;\n"
+        // this rule set is used to place the commas
+        + "%%commas:\n"
+               // for values below 100, add "and" (the apostrophe at the
+               // beginning is ignored, but causes the space that follows it
+               // to be significant: this is necessary because the rules
+               // calling %%commas don't put a space before it)
+        + "    ' and =%default=;\n"
+               // put a comma after the thousands (or whatever preceded the
+               // hundreds)
+        + "    100: , =%default=;\n"
+               // put a comma after the millions (or whatever precedes the
+               // thousands)
+        + "    1000: , <%default< thousand, >%default>;\n"
+               // and so on...
+        + "    1,000,000: , =%default=;"
+        // %%lenient-parse isn't really a set of number formatting rules;
+        // it's a set of collation rules.  Lenient-parse mode uses a Collator
+        // object to compare fragments of the text being parsed to the text
+        // in the rules, allowing more leeway in the matching text.  This set
+        // of rules tells the formatter to ignore commas when parsing (it
+        // already ignores spaces, which is why we refer to the space; it also
+        // ignores hyphens, making "twenty one" and "twenty-one" parse
+        // identically)
+        + "%%lenient-parse:\n"
+        + "    & ' ' , ',' ;\n";
+
+    /**
+     * Spellout rules for U.K. English.  U.K. English has one significant
+     * difference from U.S. English: the names for values of 1,000,000,000
+     * and higher.  In American English, each successive "-illion" is 1,000
+     * times greater than the preceding one: 1,000,000,000 is "one billion"
+     * and 1,000,000,000,000 is "one trillion."  In British English, each
+     * successive "-illion" is one million times greater than the one before:
+     * "one billion" is 1,000,000,000,000 (or what Americans would call a
+     * "trillion"), and "one trillion" is 1,000,000,000,000,000,000.
+     * 1,000,000,000 in British English is "one thousand million."  (This
+     * value is sometimes called a "milliard," but this word seems to have
+     * fallen into disuse.)
+     */
+    public static final String ukEnglish =
+        "%simplified:\n"
+        + "    -x: minus >>;\n"
+        + "    x.x: << point >>;\n"
+        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
+        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
+        + "        seventeen; eighteen; nineteen;\n"
+        + "    20: twenty[->>];\n"
+        + "    30: thirty[->>];\n"
+        + "    40: forty[->>];\n"
+        + "    50: fifty[->>];\n"
+        + "    60: sixty[->>];\n"
+        + "    70: seventy[->>];\n"
+        + "    80: eighty[->>];\n"
+        + "    90: ninety[->>];\n"
+        + "    100: << hundred[ >>];\n"
+        + "    1000: << thousand[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        + "%alt-teens:\n"
+        + "    =%simplified=;\n"
+        + "    1000>: <%%alt-hundreds<[ >>];\n"
+        + "    10,000: =%simplified=;\n"
+        + "    1,000,000: << million[ >%simplified>];\n"
+        + "    1,000,000,000,000: << billion[ >%simplified>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        + "%%alt-hundreds:\n"
+        + "    0: SHOULD NEVER GET HERE!;\n"
+        + "    10: <%simplified< thousand;\n"
+        + "    11: =%simplified= hundred>%%empty>;\n"
+        + "%%empty:\n"
+        + "    0:;"
+        + "%ordinal:\n"
+        + "    zeroth; first; second; third; fourth; fifth; sixth; seventh;\n"
+        + "        eighth; ninth;\n"
+        + "    tenth; eleventh; twelfth; thirteenth; fourteenth;\n"
+        + "        fifteenth; sixteenth; seventeenth; eighteenth;\n"
+        + "        nineteenth;\n"
+        + "    twentieth; twenty->>;\n"
+        + "    30: thirtieth; thirty->>;\n"
+        + "    40: fortieth; forty->>;\n"
+        + "    50: fiftieth; fifty->>;\n"
+        + "    60: sixtieth; sixty->>;\n"
+        + "    70: seventieth; seventy->>;\n"
+        + "    80: eightieth; eighty->>;\n"
+        + "    90: ninetieth; ninety->>;\n"
+        + "    100: <%simplified< hundredth; <%simplified< hundred >>;\n"
+        + "    1000: <%simplified< thousandth; <%simplified< thousand >>;\n"
+        + "    1,000,000: <%simplified< millionth; <%simplified< million >>;\n"
+        + "    1,000,000,000,000: <%simplified< billionth;\n"
+        + "        <%simplified< billion >>;\n"
+        + "    1,000,000,000,000,000: =#,##0=;"
+        + "%default:\n"
+        + "    -x: minus >>;\n"
+        + "    x.x: << point >>;\n"
+        + "    =%simplified=;\n"
+        + "    100: << hundred[ >%%and>];\n"
+        + "    1000: << thousand[ >%%and>];\n"
+        + "    100,000>>: << thousand[>%%commas>];\n"
+        + "    1,000,000: << million[>%%commas>];\n"
+        + "    1,000,000,000,000: << billion[>%%commas>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        + "%%and:\n"
+        + "    and =%default=;\n"
+        + "    100: =%default=;\n"
+        + "%%commas:\n"
+        + "    ' and =%default=;\n"
+        + "    100: , =%default=;\n"
+        + "    1000: , <%default< thousand, >%default>;\n"
+        + "    1,000,000: , =%default=;"
+        + "%%lenient-parse:\n"
+        + "    & ' ' , ',' ;\n";
+    // Could someone please correct me if I'm wrong about "milliard" falling
+    // into disuse, or have missed any other details of how large numbers
+    // are rendered.  Also, could someone please provide me with information
+    // on which other English-speaking countries use which system?  Right now,
+    // I'm assuming that the U.S. system is used in Canada and that all the
+    // other English-speaking countries follow the British system.  Can
+    // someone out there confirm this?
+
+    /**
+     * Spellout rules for Spanish.  The Spanish rules are quite similar to
+     * the English rules, but there are some important differences:
+     * First, we have to provide separate rules for most of the twenties
+     * because the ones digit frequently picks up an accent mark that it
+     * doesn't have when standing alone.  Second, each multiple of 100 has
+     * to be specified separately because the multiplier on 100 very often
+     * changes form in the contraction: 500 is "quinientos," not
+     * "cincocientos."  In addition, the word for 100 is "cien" when
+     * standing alone, but changes to "ciento" when followed by more digits.
+     * There also some other differences.
+     */
+    public static final String spanish =
+        // negative-number and fraction rules
+        "-x: menos >>;\n"
+        + "x.x: << punto >>;\n"
+        // words for values from 0 to 19
+        + "cero; uno; dos; tres; cuatro; cinco; seis; siete; ocho; nueve;\n"
+        + "diez; once; doce; trece; catorce; quince; diecis\u00e9is;\n"
+        + "    diecisiete; dieciocho; diecinueve;\n"
+        // words for values from 20 to 29 (necessary because the ones digit
+        // often picks up an accent mark it doesn't have when standing alone)
+        + "veinte; veintiuno; veintid\u00f3s; veintitr\u00e9s; veinticuatro;\n"
+        + "    veinticinco; veintis\u00e9is; veintisiete; veintiocho;\n"
+        + "    veintinueve;\n"
+        // words for multiples of 10 (notice that the tens digit is separated
+        // from the ones digit by the word "y".)
+        + "30: treinta[ y >>];\n"
+        + "40: cuarenta[ y >>];\n"
+        + "50: cincuenta[ y >>];\n"
+        + "60: sesenta[ y >>];\n"
+        + "70: setenta[ y >>];\n"
+        + "80: ochenta[ y >>];\n"
+        + "90: noventa[ y >>];\n"
+        // 100 by itself is "cien," but 100 followed by something is "cineto"
+        + "100: cien;\n"
+        + "101: ciento >>;\n"
+        // words for multiples of 100 (must be stated because they're
+        // rarely simple concatenations)
+        + "200: doscientos[ >>];\n"
+        + "300: trescientos[ >>];\n"
+        + "400: cuatrocientos[ >>];\n"
+        + "500: quinientos[ >>];\n"
+        + "600: seiscientos[ >>];\n"
+        + "700: setecientos[ >>];\n"
+        + "800: ochocientos[ >>];\n"
+        + "900: novecientos[ >>];\n"
+        // for 1,000, the multiplier on "mil" is omitted: 2,000 is "dos mil,"
+        // but 1,000 is just "mil."
+        + "1000: mil[ >>];\n"
+        + "2000: << mil[ >>];\n"
+        // 1,000,000 is "un millon," not "uno millon"
+        + "1,000,000: un mill\u00f3n[ >>];\n"
+        + "2,000,000: << mill\u00f3n[ >>];\n"
+        // overflow rule
+        + "1,000,000,000: =#,##0= (incomplete data);";
+    // The Spanish rules are incomplete.  I'm missing information on negative
+    // numbers and numbers with fractional parts.  I also don't have
+    // information on numbers higher than the millions
+
+    /**
+     * Spellout rules for French.  French adds some interesting quirks of its
+     * own: 1) The word "et" is interposed between the tens and ones digits,
+     * but only if the ones digit if 1: 20 is "vingt," and 2 is "vingt-deux,"
+     * but 21 is "vingt-et-un."  2)  There are no words for 70, 80, or 90.
+     * "quatre-vingts" ("four twenties") is used for 80, and values proceed
+     * by score from 60 to 99 (e.g., 73 is "soixante-treize" ["sixty-thirteen"]).
+     * Numbers from 1,100 to 1,199 are rendered as hundreds rather than
+     * thousands: 1,100 is "onze cents" ("eleven hundred"), rather than
+     * "mille cent" ("one thousand one hundred")
+     */
+    public static final String french =
+        // the main rule set
+        "%main:\n"
+               // negative-number and fraction rules
+        + "    -x: moins >>;\n"
+        + "    x.x: << virgule >>;\n"
+               // words for numbers from 0 to 10
+        + "    z\u00e9ro; un; deux; trois; quatre; cinq; six; sept; huit; neuf;\n"
+        + "    dix; onze; douze; treize; quatorze; quinze; seize;\n"
+        + "        dix-sept; dix-huit; dix-neuf;\n"
+               // ords for the multiples of 10: %%alt-ones inserts "et"
+               // when needed
+        + "    20: vingt[->%%alt-ones>];\n"
+        + "    30: trente[->%%alt-ones>];\n"
+        + "    40: quarante[->%%alt-ones>];\n"
+        + "    50: cinquante[->%%alt-ones>];\n"
+               // rule for 60.  The /20 causes this rule's multiplier to be
+               // 20 rather than 10, allowinhg us to recurse for all values
+               // from 60 to 79...
+        + "    60/20: soixante[->%%alt-ones>];\n"
+               // ...except for 71, which must be special-cased
+        + "    71: soixante et onze;\n"
+               // at 72, we have to repeat the rule for 60 to get us to 79
+        + "    72/20: soixante->%%alt-ones>;\n"
+               // at 80, we state a new rule with the phrase for 80.  Since
+               // it changes form when there's a ones digit, we need a second
+               // rule at 81.  This rule also includes "/20," allowing it to
+               // be used correctly for all values up to 99
+        + "    80: quatre-vingts; 81/20: quatre-vingt->>;\n"
+               // "cent" becomes plural when preceded by a multiplier, and
+               // the multiplier is omitted from the singular form
+        + "    100: cent[ >>];\n"
+        + "    200: << cents[ >>];\n"
+        + "    1000: mille[ >>];\n"
+               // values from 1,100 to 1,199 are rendered as "onze cents..."
+               // instead of "mille cent..."  The > after "1000" decreases
+               // the rule's exponent, causing its multiplier to be 100 instead
+               // of 1,000.  This prevents us from getting "onze cents cent
+               // vingt-deux" ("eleven hundred one hundred twenty-two").
+        + "    1100>: onze cents[ >>];\n"
+               // at 1,200, we go back to formating in thousands, so we
+               // repeat the rule for 1,000
+        + "    1200: mille >>;\n"
+               // at 2,000, the multiplier is added
+        + "    2000: << mille[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000: << milliarde[ >>];\n"
+        + "    1,000,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        // %%alt-ones is used to insert "et" when the ones digit is 1
+        + "%%alt-ones:\n"
+        + "    ; et-un; =%main=;";
+
+    /**
+     * Spellout rules for Swiss French.  Swiss French differs from French French
+     * in that it does have words for 70, 80, and 90.  This rule set shows them,
+     * and is simpler as a result.
+     */
+    public static final String swissFrench =
+        "%main:\n"
+        + "    -x: moins >>;\n"
+        + "    x.x: << virgule >>;\n"
+        + "    z\u00e9ro; un; deux; trois; quatre; cinq; six; sept; huit; neuf;\n"
+        + "    dix; onze; douze; treize; quatorze; quinze; seize;\n"
+        + "        dix-sept; dix-huit; dix-neuf;\n"
+        + "    20: vingt[->%%alt-ones>];\n"
+        + "    30: trente[->%%alt-ones>];\n"
+        + "    40: quarante[->%%alt-ones>];\n"
+        + "    50: cinquante[->%%alt-ones>];\n"
+        + "    60: soixante[->%%alt-ones>];\n"
+               // notice new words for 70, 80, and 90
+        + "    70: septante[->%%alt-ones>];\n"
+        + "    80: octante[->%%alt-ones>];\n"
+        + "    90: nonante[->%%alt-ones>];\n"
+        + "    100: cent[ >>];\n"
+        + "    200: << cents[ >>];\n"
+        + "    1000: mille[ >>];\n"
+        + "    1100>: onze cents[ >>];\n"
+        + "    1200: mille >>;\n"
+        + "    2000: << mille[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000: << milliarde[ >>];\n"
+        + "    1,000,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        + "%%alt-ones:\n"
+        + "    ; et-un; =%main=;";
+    // I'm not 100% sure about Swiss French.  Is
+    // this correct?  Is "onze cents" commonly used for 1,100 in both France
+    // and Switzerland?  Can someone fill me in on the rules for the other
+    // French-speaking countries?  I've heard conflicting opinions on which
+    // version is used in Canada, and I understand there's an alternate set
+    // of words for 70, 80, and 90 that is used somewhere, but I don't know
+    // what those words are or where they're used.
+
+    /**
+     * Spellout rules for German.  German also adds some interesting
+     * characteristics.  For values below 1,000,000, numbers are customarily
+     * written out as a single word.  And the ones digit PRECEDES the tens
+     * digit (e.g., 23 is "dreiundzwanzig," not "zwanzigunddrei").
+     */
+    public static final String german =
+        // 1 is "eins" when by itself, but turns into "ein" in most
+        // combinations
+        "%alt-ones:\n"
+        + "    null; eins; =%%main=;\n"
+        + "%%main:\n"
+               // words for numbers from 0 to 12.  Notice that the values
+               // from 13 to 19 can derived algorithmically, unlike in most
+               // other languages
+        + "    null; ein; zwei; drei; vier; f\u00fcnf; sechs; sieben; acht; neun;\n"
+        + "    zehn; elf; zw\u00f6lf; >>zehn;\n"
+               // rules for the multiples of 10.  Notice that the ones digit
+               // goes on the front
+        + "    20: [>>und]zwanzig;\n"
+        + "    30: [>>und]drei\u00dfig;\n"
+        + "    40: [>>und]vierzig;\n"
+        + "    50: [>>und]f\u00fcnfzig;\n"
+        + "    60: [>>und]sechzig;\n"
+        + "    70: [>>und]siebzig;\n"
+        + "    80: [>>und]achtzig;\n"
+        + "    90: [>>und]neunzig;\n"
+        + "    100: hundert[>%alt-ones>];\n"
+        + "    200: <<hundert[>%alt-ones>];\n"
+        + "    1000: tausend[>%alt-ones>];\n"
+        + "    2000: <<tausend[>%alt-ones>];\n"
+        + "    1,000,000: eine Million[ >%alt-ones>];\n"
+        + "    2,000,000: << Millionen[ >%alt-ones>];\n"
+        + "    1,000,000,000: eine Milliarde[ >%alt-ones>];\n"
+        + "    2,000,000,000: << Milliarden[ >%alt-ones>];\n"
+        + "    1,000,000,000,000: eine Billion[ >%alt-ones>];\n"
+        + "    2,000,000,000,000: << Billionen[ >%alt-ones>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;";
+    // again, I'm not 100% sure of these rules.  I think both "hundert" and
+    // "einhundert" are correct or 100, but I'm not sure which is preferable
+    // in situations where this framework is likely to be used.  Also, is it
+    // really true that numbers are run together into compound words all the
+    // time?  And again, I'm missing information on negative numbers and
+    // decimas.
+
+    /**
+     * Spellout rules for Italian.  Like German, most Italian numbers are
+     * written as single words.  What makes these rules complicated is the rule
+     * that says that when a word ending in a vowel and a word beginning with
+     * a vowel are combined into a compound, the vowel is dropped from the
+     * end of the first word: 180 is "centottanta," not "centoottanta."
+     * The complexity of this rule set is to produce this behavior.
+     */
+    public static final String italian =
+        // main rule set.  Follows the patterns of the preceding rule sets,
+        // except that the final vowel is omitted from words ending in
+        // vowels when they are followed by another word; instead, we have
+        // separate rule sets that are identical to this one, except that
+        // all the words that don't begin with a vowel have a vowel tacked
+        // onto them at the front.  A word ending in a vowel calls a
+        // substitution that will supply that vowel, unless that vowel is to
+        // be elided.
+        "%main:\n"
+        + "    -x: meno >>;\n"
+        + "    x.x: << virgola >>;\n"
+        + "    zero; uno; due; tre; quattro; cinque; sei; sette; otto;\n"
+        + "        nove;\n"
+        + "    dieci; undici; dodici; tredici; quattordici; quindici; sedici;\n"
+        + "        diciasette; diciotto; diciannove;\n"
+        + "    20: venti; vent>%%with-i>;\n"
+        + "    30: trenta; trent>%%with-i>;\n"
+        + "    40: quaranta; quarant>%%with-a>;\n"
+        + "    50: cinquanta; cinquant>%%with-a>;\n"
+        + "    60: sessanta; sessant>%%with-a>;\n"
+        + "    70: settanta; settant>%%with-a>;\n"
+        + "    80: ottanta; ottant>%%with-a>;\n"
+        + "    90: novanta; novant>%%with-a>;\n"
+        + "    100: cento; cent[>%%with-o>];\n"
+        + "    200: <<cento; <<cent[>%%with-o>];\n"
+        + "    1000: mille; mill[>%%with-i>];\n"
+        + "    2000: <<mila; <<mil[>%%with-a>];\n"
+        + "    100,000>>: <<mila[ >>];\n"
+        + "    1,000,000: =#,##0= (incomplete data);\n"
+        + "%%with-a:\n"
+        + "    azero; uno; adue; atre; aquattro; acinque; asei; asette; otto;\n"
+        + "        anove;\n"
+        + "    adieci; undici; adodici; atredici; aquattordici; aquindici; asedici;\n"
+        + "        adiciasette; adiciotto; adiciannove;\n"
+        + "    20: aventi; avent>%%with-i>;\n"
+        + "    30: atrenta; atrent>%%with-i>;\n"
+        + "    40: aquaranta; aquarant>%%with-a>;\n"
+        + "    50: acinquanta; acinquant>%%with-a>;\n"
+        + "    60: asessanta; asessant>%%with-a>;\n"
+        + "    70: asettanta; asettant>%%with-a>;\n"
+        + "    80: ottanta; ottant>%%with-a>;\n"
+        + "    90: anovanta; anovant>%%with-a>;\n"
+        + "    100: acento; acent[>%%with-o>];\n"
+        + "    200: <%%with-a<cento; <%%with-a<cent[>%%with-o>];\n"
+        + "    1000: amille; amill[>%%with-i>];\n"
+        + "    2000: <%%with-a<mila; <%%with-a<mil[>%%with-a>];\n"
+        + "    100,000: =%main=;\n"
+        + "%%with-i:\n"
+        + "    izero; uno; idue; itre; iquattro; icinque; isei; isette; otto;\n"
+        + "        inove;\n"
+        + "    idieci; undici; idodici; itredici; iquattordici; iquindici; isedici;\n"
+        + "        idiciasette; idiciotto; idiciannove;\n"
+        + "    20: iventi; ivent>%%with-i>;\n"
+        + "    30: itrenta; itrent>%%with-i>;\n"
+        + "    40: iquaranta; iquarant>%%with-a>;\n"
+        + "    50: icinquanta; icinquant>%%with-a>;\n"
+        + "    60: isessanta; isessant>%%with-a>;\n"
+        + "    70: isettanta; isettant>%%with-a>;\n"
+        + "    80: ottanta; ottant>%%with-a>;\n"
+        + "    90: inovanta; inovant>%%with-a>;\n"
+        + "    100: icento; icent[>%%with-o>];\n"
+        + "    200: <%%with-i<cento; <%%with-i<cent[>%%with-o>];\n"
+        + "    1000: imille; imill[>%%with-i>];\n"
+        + "    2000: <%%with-i<mila; <%%with-i<mil[>%%with-a>];\n"
+        + "    100,000: =%main=;\n"
+        + "%%with-o:\n"
+        + "    ozero; uno; odue; otre; oquattro; ocinque; osei; osette; otto;\n"
+        + "        onove;\n"
+        + "    odieci; undici; ododici; otredici; oquattordici; oquindici; osedici;\n"
+        + "        odiciasette; odiciotto; odiciannove;\n"
+        + "    20: oventi; ovent>%%with-i>;\n"
+        + "    30: otrenta; otrent>%%with-i>;\n"
+        + "    40: oquaranta; oquarant>%%with-a>;\n"
+        + "    50: ocinquanta; ocinquant>%%with-a>;\n"
+        + "    60: osessanta; osessant>%%with-a>;\n"
+        + "    70: osettanta; osettant>%%with-a>;\n"
+        + "    80: ottanta; ottant>%%with-a>;\n"
+        + "    90: onovanta; onovant>%%with-a>;\n"
+        + "    100: ocento; ocent[>%%with-o>];\n"
+        + "    200: <%%with-o<cento; <%%with-o<cent[>%%with-o>];\n"
+        + "    1000: omille; omill[>%%with-i>];\n"
+        + "    2000: <%%with-o<mila; <%%with-o<mil[>%%with-a>];\n"
+        + "    100,000: =%main=;\n";
+    // Can someone confirm that I did the vowel-eliding thing right?  I'm
+    // not 100% sure I'm doing it in all the right places, or completely
+    // correctly.  Also, I don't have information for negatives and decimals,
+    // and I lack words fror values from 1,000,000 on up.
+
+    /**
+     * Spellout rules for Swedish.
+     */
+    public static final String swedish =
+        "noll; ett; tv\u00e5; tre; fyra; fem; sex; sjo; \u00e5tta; nio;\n"
+        + "tio; elva; tolv; tretton; fjorton; femton; sexton; sjutton; arton; nitton;\n"
+        + "20: tjugo[>>];\n"
+        + "30: trettio[>>];\n"
+        + "40: fyrtio[>>];\n"
+        + "50: femtio[>>];\n"
+        + "60: sextio[>>];\n"
+        + "70: sjuttio[>>];\n"
+        + "80: \u00e5ttio[>>];\n"
+        + "90: nittio[>>];\n"
+        + "100: hundra[>>];\n"
+        + "200: <<hundra[>>];\n"
+        + "1000: tusen[ >>];\n"
+        + "2000: << tusen[ >>];\n"
+        + "1,000,000: en miljon[ >>];\n"
+        + "2,000,000: << miljon[ >>];\n"
+        + "1,000,000,000: en miljard[ >>];\n"
+        + "2,000,000,000: << miljard[ >>];\n"
+        + "1,000,000,000,000: en biljon[ >>];\n"
+        + "2,000,000,000,000: << biljon[ >>];\n"
+        + "1,000,000,000,000,000: =#,##0=";
+    // can someone supply me with information on negatives and decimals?
+
+    /**
+     * Spellout rules for Dutch.  Notice that in Dutch, as in German,
+     * the ones digit precedes the tens digit.
+     */
+    public static final String dutch =
+        " -x: min >>;\n"
+        + "x.x: << komma >>;\n"
+        + "(zero?); een; twee; drie; vier; vijf; zes; zeven; acht; negen;\n"
+        + "tien; elf; twaalf; dertien; veertien; vijftien; zestien;\n"
+        + "zeventien; achtien; negentien;\n"
+        + "20: [>> en ]twintig;\n"
+        + "30: [>> en ]dertig;\n"
+        + "40: [>> en ]veertig;\n"
+        + "50: [>> en ]vijftig;\n"
+        + "60: [>> en ]zestig;\n"
+        + "70: [>> en ]zeventig;\n"
+        + "80: [>> en ]tachtig;\n"
+        + "90: [>> en ]negentig;\n"
+        + "100: << honderd[ >>];\n"
+        + "1000: << duizend[ >>];\n"
+        + "1,000,000: << miljoen[ >>];\n"
+        + "1,000,000,000: << biljoen[ >>];\n"
+        + "1,000,000,000,000: =#,##0=";
+
+    /**
+     * Spellout rules for Japanese.  In Japanese, there really isn't any
+     * distinction between a number written out in digits and a number
+     * written out in words: the ideographic characters are both digits
+     * and words.  This rule set provides two variants:  %traditional
+     * uses the traditional CJK numerals (which are also used in China
+     * and Korea).  %financial uses alternate ideographs for many numbers
+     * that are harder to alter than the traditional numerals (one could
+     * fairly easily change a one to
+     * a three just by adding two strokes, for example).  This is also done in
+     * the other countries using Chinese idographs, but different ideographs
+     * are used in those places.
+     */
+    public static final String japanese =
+        "%financial:\n"
+        + "    \u96f6; \u58f1; \u5f10; \u53c2; \u56db; \u4f0d; \u516d; \u4e03; \u516b; \u4e5d;\n"
+        + "    \u62fe[>>];\n"
+        + "    20: <<\u62fe[>>];\n"
+        + "    100: <<\u767e[>>];\n"
+        + "    1000: <<\u5343[>>];\n"
+        + "    10,000: <<\u4e07[>>];\n"
+        + "    100,000,000: <<\u5104[>>];\n"
+        + "    1,000,000,000,000: <<\u5146[>>];\n"
+        + "    10,000,000,000,000,000: =#,##0=;\n"
+        + "%traditional:\n"
+        + "    \u96f6; \u4e00; \u4e8c; \u4e09; \u56db; \u4e94; \u516d; \u4e03; \u516b; \u4e5d;\n"
+        + "    \u5341[>>];\n"
+        + "    20: <<\u5341[>>];\n"
+        + "    100: <<\u767e[>>];\n"
+        + "    1000: <<\u5343[>>];\n"
+        + "    10,000: <<\u4e07[>>];\n"
+        + "    100,000,000: <<\u5104[>>];\n"
+        + "    1,000,000,000,000: <<\u5146[>>];\n"
+        + "    10,000,000,000,000,000: =#,##0=;";
+    // Can someone supply me with the right fraud-proof ideographs for
+    // Simplified and Traditional Chinese, and for Korean?  Can someone
+    // supply me with information on negatives and decimals?
+
+    /**
+     * Spellout rules for Greek.  Again in Greek we have to supply the words
+     * for the multiples of 100 because they can't be derived algorithmically.
+     * Also, the tens dgit changes form when followed by a ones digit: an
+     * accent mark disappears from the tens digit and moves to the ones digit.
+     * Therefore, instead of using the [] notation, we actually have to use
+     * two separate rules for each multiple of 10 to show the two forms of
+     * the word.
+     */
+    public static final String greek =
+        "zero (incomplete data); \u03ad\u03bd\u03b1; \u03b4\u03cd\u03bf; \u03b4\u03c1\u03af\u03b1; "
+        + "\u03c4\u03ad\u03c3\u03c3\u03b5\u03c1\u03b1; \u03c0\u03ad\u03bd\u03c4\u03b5; "
+        + "\u03ad\u03be\u03b9; \u03b5\u03c0\u03c4\u03ac; \u03bf\u03ba\u03c4\u03ce; "
+        + "\u03b5\u03bd\u03bd\u03ad\u03b1;\n"
+        + "10: \u03b4\u03ad\u03ba\u03b1; "
+        + "\u03ad\u03bd\u03b4\u03b5\u03ba\u03b1; \u03b4\u03ce\u03b4\u03b5\u03ba\u03b1; "
+        + "\u03b4\u03b5\u03ba\u03b1>>;\n"
+        + "20: \u03b5\u03af\u03ba\u03bf\u03c3\u03b9; \u03b5\u03b9\u03ba\u03bf\u03c3\u03b9>>;\n"
+        + "30: \u03c4\u03c1\u03b9\u03ac\u03bd\u03c4\u03b1; \u03c4\u03c1\u03b9\u03b1\u03bd\u03c4\u03b1>>;\n"
+        + "40: \u03c3\u03b1\u03c1\u03ac\u03bd\u03c4\u03b1; \u03c3\u03b1\u03c1\u03b1\u03bd\u03c4\u03b1>>;\n"
+        + "50: \u03c0\u03b5\u03bd\u03ae\u03bd\u03c4\u03b1; \u03c0\u03b5\u03bd\u03b7\u03bd\u03c4\u03b1>>;\n"
+        + "60: \u03b5\u03be\u03ae\u03bd\u03c4\u03b1; \u03b5\u03be\u03b7\u03bd\u03c4\u03b1>>;\n"
+        + "70: \u03b5\u03b2\u03b4\u03bf\u03bc\u03ae\u03bd\u03c4\u03b1; "
+        + "\u03b5\u03b2\u03b4\u03bf\u03bc\u03b7\u03bd\u03c4\u03b1>>;\n"
+        + "80: \u03bf\u03b3\u03b4\u03cc\u03bd\u03c4\u03b1; \u03bf\u03b3\u03b4\u03bf\u03bd\u03c4\u03b1>>;\n"
+        + "90: \u03b5\u03bd\u03bd\u03b5\u03bd\u03ae\u03bd\u03c4\u03b1; "
+        + "\u03b5\u03bd\u03bd\u03b5\u03bd\u03b7\u03bd\u03c4\u03b1>>;\n"
+        + "100: \u03b5\u03ba\u03b1\u03c4\u03cc[\u03bd >>];\n"
+        + "200: \u03b4\u03b9\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "300: \u03c4\u03c1\u03b9\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "400: \u03c4\u03b5\u03c4\u03c1\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "500: \u03c0\u03b5\u03bd\u03c4\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "600: \u03b5\u03be\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "700: \u03b5\u03c0\u03c4\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "800: \u03bf\u03ba\u03c4\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "900: \u03b5\u03bd\u03bd\u03b9\u03b1\u03ba\u03cc\u03c3\u03b9\u03b1[ >>];\n"
+        + "1000: \u03c7\u03af\u03bb\u03b9\u03b1[ >>];\n"
+        + "2000: << \u03c7\u03af\u03bb\u03b9\u03b1[ >>];\n"
+        + "1,000,000: << \u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03b9\u03cc\u03c1\u03b9\u03bf[ >>];\n"
+        + "1,000,000,000: << \u03b4\u03b9\u03c3\u03b5\u03ba\u03b1\u03c4\u03bf\u03bc\u03bc\u03b9\u03cc\u03c1\u03b9\u03bf[ >>];\n"
+        + "1,000,000,000,000: =#,##0=";
+    // Can someone supply me with information on negatives and decimals?
+    // I'm also missing the word for zero.  Can someone clue me in?
+
+    /**
+     * Spellout rules for Russian.
+     */
+    public static final String russian =
+        "\u043d\u043e\u043b\u044c; \u043e\u0434\u0438\u043d; \u0434\u0432\u0430; \u0442\u0440\u0438; "
+        + "\u0447\u0435\u0442\u044b\u0440\u0435; \u043f\u044f\u0442; \u0448\u0435\u0441\u0442; "
+        + "\u0441\u0435\u043c\u044c; \u0432\u043e\u0441\u0435\u043c\u044c; \u0434\u0435\u0432\u044f\u0442;\n"
+        + "10: \u0434\u0435\u0441\u044f\u0442; "
+        + "\u043e\u0434\u0438\u043d\u043d\u0430\u0434\u0446\u0430\u0442\u044c;\n"
+        + "\u0434\u0432\u0435\u043d\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0442\u0440\u0438\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0447\u0435\u0442\u044b\u0440\u043d\u0430\u0434\u0446\u0430\u0442\u044c;\n"
+        + "15: \u043f\u044f\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0441\u0435\u043c\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0432\u043e\u0441\u0435\u043c\u043d\u0430\u0434\u0446\u0430\u0442\u044c; "
+        + "\u0434\u0435\u0432\u044f\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c;\n"
+        + "20: \u0434\u0432\u0430\u0434\u0446\u0430\u0442\u044c[ >>];\n"
+        + "30: \u0442\u0440\u043b\u0434\u0446\u0430\u0442\u044c[ >>];\n"
+        + "40: \u0441\u043e\u0440\u043e\u043a[ >>];\n"
+        + "50: \u043f\u044f\u0442\u044c\u0434\u0435\u0441\u044f\u0442[ >>];\n"
+        + "60: \u0448\u0435\u0441\u0442\u044c\u0434\u0435\u0441\u044f\u0442[ >>];\n"
+        + "70: \u0441\u0435\u043c\u044c\u0434\u0435\u0441\u044f\u0442[ >>];\n"
+        + "80: \u0432\u043e\u0441\u0435\u043c\u044c\u0434\u0435\u0441\u044f\u0442[ >>];\n"
+        + "90: \u0434\u0435\u0432\u044f\u043d\u043e\u0441\u0442\u043e[ >>];\n"
+        + "100: \u0441\u0442\u043e[ >>];\n"
+        + "200: << \u0441\u0442\u043e[ >>];\n"
+        + "1000: \u0442\u044b\u0441\u044f\u0447\u0430[ >>];\n"
+        + "2000: << \u0442\u044b\u0441\u044f\u0447\u0430[ >>];\n"
+        + "1,000,000: \u043c\u0438\u043b\u043b\u0438\u043e\u043d[ >>];\n"
+        + "2,000,000: << \u043c\u0438\u043b\u043b\u0438\u043e\u043d[ >>];\n"
+        + "1,000,000,000: =#,##0=;";
+    // Can someone supply me with information on negatives and decimals?
+    // How about words for billions and trillions?
+
+    /**
+     * Spellout rules for Hebrew.  Hebrew actually has inflected forms for
+     * most of the lower-order numbers.  The masculine forms are shown
+     * here.
+     */
+    public static final String hebrew =
+        "zero (incomplete data); \u05d0\u05d4\u05d3; \u05e9\u05d2\u05d9\u05d9\u05dd; \u05e9\u05dc\u05d5\u05e9\u05d4;\n"
+        + "4: \u05d0\u05d3\u05d1\u05e6\u05d4; \u05d7\u05d2\u05d5\u05d9\u05e9\u05d4; \u05e9\u05e9\u05d4;\n"
+        + "7: \u05e9\u05d1\u05e6\u05d4; \u05e9\u05de\u05d5\u05d2\u05d4; \u05ea\u05e9\u05e6\u05d4;\n"
+        + "10: \u05e6\u05e9\u05d3\u05d4[ >>];\n"
+        + "20: \u05e6\u05e9\u05d3\u05d9\u05dd[ >>];\n"
+        + "30: \u05e9\u05dc\u05d5\u05e9\u05d9\u05dd[ >>];\n"
+        + "40: \u05d0\u05d3\u05d1\u05e6\u05d9\u05dd[ >>];\n"
+        + "50: \u05d7\u05de\u05d9\u05e9\u05d9\u05dd[ >>];\n"
+        + "60: \u05e9\u05e9\u05d9\u05dd[ >>];\n"
+        + "70: \u05e9\u05d1\u05e6\u05d9\u05dd[ >>];\n"
+        + "80: \u05e9\u05de\u05d5\u05d2\u05d9\u05dd[ >>];\n"
+        + "90: \u05ea\u05e9\u05e6\u05d9\u05dd[ >>];\n"
+        + "100: \u05de\u05d0\u05d4[ >>];\n"
+        + "200: << \u05de\u05d0\u05d4[ >>];\n"
+        + "1000: \u05d0\u05dc\u05e3[ >>];\n"
+        + "2000: << \u05d0\u05dc\u05e3[ >>];\n"
+        + "1,000,000: =#,##0= (incomplete data);";
+    // This data is woefully incomplete.  Can someone fill me in on the
+    // various inflected forms of the numbers, which seem to be necessary
+    // to do Hebrew correctly?  Can somone supply me with data for values
+    // from 1,000,000 on up?  What about the word for zero?  What about
+    // information on negatives and decimals?
+
+    //========================================================================
+    // Simple examples
+    //========================================================================
+
+    /**
+     * This rule set adds an English ordinal abbreviation to the end of a
+     * number.  For example, 2 is formatted as "2nd".  Parsing doesn't work with
+     * this rule set.  To parse, use DecimalFormat on the numeral.
+     */
+    public static final String ordinal =
+        // this rule set formats the numeral and calls %%abbrev to
+        // supply the abbreviation
+        "%main:\n"
+        + "    =#,##0==%%abbrev=;\n"
+        // this rule set supplies the abbreviation
+        + "%%abbrev:\n"
+               // the abbreviations.  Everything from 4 to 19 ends in "th"
+        + "    th; st; nd; rd; th;\n"
+               // at 20, we begin repeating the cycle every 10 (13 is "13th",
+               // but 23 and 33 are "23rd" and "33rd")  We do this by
+               // ignoring all bug the ones digit in selecting the abbreviation
+        + "    20: >>;\n"
+               // at 100, we repeat the whole cycle by considering only the
+               // tens and ones digits in picking an abbreviation
+        + "    100: >>;\n";
+
+    /**
+     * This is a simple message-formatting example.  Normally one would
+     * use ChoiceFormat and MessageFormat to do something this simple,
+     * but this shows it could be done with RuleBasedNumberFormat too.
+     * A message-formatting example that might work better with
+     * RuleBasedNumberFormat appears later.
+     */
+    public static final String message1 =
+        // this rule surrounds whatever the other rules produce with the
+        // rest of the sentence
+        "x.0: The search found <<.;\n"
+        // use words for values below 10 (and change to "file" for 1)
+        + "no files; one file; two files; three files; four files; five files;\n"
+        + "    six files; seven files; eight files; nine files;\n"
+        // use numerals for values higher than 10
+        + "=#,##0= files;";
+
+    //========================================================================
+    // Fraction handling
+    //
+    // The next few examples show how RuleBasedNumberFormat can be used for
+    // more flexible handling of fractions
+    //========================================================================
+
+    /**
+     * This example formats a number in one of the two styles often used
+     * on checks.  %dollars-and-hundredths formats cents as hundredths of
+     * a dollar (23.40 comes out as "twenty-three and 40/100 dollars").
+     * %dollars-and-cents formats in dollars and cents (23.40 comes out as
+     * "twenty-three dollars and forty cents")
+     */
+    public static final String dollarsAndCents =
+        // this rule set formats numbers as dollars and cents
+        "%dollars-and-cents:\n"
+               // if the value is 1 or more, put "xx dollars and yy cents".
+               // the "and y cents" part is suppressed if the value is an
+               // even number of dollars
+        + "    x.0: << [and >%%cents>];\n"
+               // if the value is between 0 and 1, put "xx cents"
+        + "    0.x: >%%cents>;\n"
+               // these three rules take care of the singular and plural
+               // forms of "dollar" and use %%main to format the number
+        + "    0: zero dollars; one dollar; =%%main= dollars;\n"
+        // these are the regular U.S. English number spellout rules
+        + "%%main:\n"
+        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
+        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
+        + "        seventeen; eighteen; nineteen;\n"
+        + "    20: twenty[->>];\n"
+        + "    30: thirty[->>];\n"
+        + "    40: forty[->>];\n"
+        + "    50: fifty[->>];\n"
+        + "    60: sixty[->>];\n"
+        + "    70: seventy[->>];\n"
+        + "    80: eighty[->>];\n"
+        + "    90: ninety[->>];\n"
+        + "    100: << hundred[ >>];\n"
+        + "    1000: << thousand[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000: << trillion[ >>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        // this rule takes care of the fractional part of the value.  It
+        // multiplies the fractional part of the number being formatted by
+        // 100, formats it with %%main, and then addes the word "cent" or
+        // "cents" to the end.  (The text in brackets is omitted if the
+        // numerator of the fraction is 1.)
+        + "%%cents:\n"
+        + "    100: <%%main< cent[s];\n"
+
+        // this rule set formats numbers as dollars and hundredths of dollars
+        + "%dollars-and-hundredths:\n"
+               // this rule takes care of the general shell of the output
+               // string.  We always show the cents, even when there aren't
+               // any.  Because of this, the word is always "dollars"--
+               // we don't have to worry about the singular form.  We use
+               // %%main to format the number of dollars and %%hundredths to
+               // format the number of cents
+        + "    x.0: <%%main< and >%%hundredths>/100 dollars;\n"
+        // this rule set formats the cents for %dollars-and-hundredths.
+        // It multiplies the fractional part of the number by 100 and formats
+        // the result using a DecimalFormat ("00" tells the DecimalFormat to
+        // always use two digits, even for numbers under 10)
+        + "%%hundredths:\n"
+        + "    100: <00<;\n";
+
+    /**
+     * This rule set shows the fractional part of the number as a fraction
+     * with a power of 10 as the denominator.  Some languages don't spell
+     * out the fractional part of a number as "point one two three," but
+     * always render it as a fraction.  If we still want to treat the fractional
+     * part of the number as a decimal, then the fraction's denominator
+     * is always a power of 10.  This example does that: 23.125 is formatted
+     * as "twenty-three and one hundred twenty-five thousandths" (as opposed
+     * to "twenty-three point one two five" or "twenty-three and one eighth").
+     */
+    public static final String decimalAsFraction =
+        // the regular U.S. English spellout rules, with one difference
+        "%main:\n"
+        + "    -x: minus >>;\n"
+               // the difference.  This rule uses %%frac to show the fractional
+               // part of the number.  Text in brackets is omitted when the
+               // value is between 0 and 1 (causing 0.3 to come out as "three
+               // tenths" instead of "zero and three tenths").
+        + "    x.x: [<< and ]>%%frac>;\n"
+        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
+        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
+        + "        seventeen; eighteen; nineteen;\n"
+        + "    twenty[->>];\n"
+        + "    30: thirty[->>];\n"
+        + "    40: forty[->>];\n"
+        + "    50: fifty[->>];\n"
+        + "    60: sixty[->>];\n"
+        + "    70: seventy[->>];\n"
+        + "    80: eighty[->>];\n"
+        + "    90: ninety[->>];\n"
+        + "    100: << hundred[ >>];\n"
+        + "    1000: << thousand[ >>];\n"
+        + "    1,000,000: << million[ >>];\n"
+        + "    1,000,000,000: << billion[ >>];\n"
+        + "    1,000,000,000,000: << trillion[ >>];\n"
+        + "    1,000,000,000,000,000: =#,##0=;\n"
+        // the rule set that formats the fractional part of the number.
+        // The rule that is used is the one that, when its baase value is
+        // multiplied by the fractional part of the number being formatted,
+        // produces the result closest to zero.  Thus, the base values are
+        // prospective denominators of the fraction.  The << marks the place
+        // where the numerator of the fraction (the result of multiplying the
+        // fractional part of the number by the rule's base value) is
+        // placed.  Text in brackets is omitted when the numerator is 1, giving
+        // us the singular and plural forms of the words.
+        // [In languages where the singular and plural are completely different
+        // words, the rule can just be stated twice: the second time with
+        // the plural form.]
+        + "%%frac:\n"
+        + "    10: << tenth[s];\n"
+        + "    100: << hundredth[s];\n"
+        + "    1000: << thousandth[s];\n"
+        + "    10,000: << ten-thousandth[s];\n"
+        + "    100,000: << hundred-thousandth[s];\n"
+        + "    1,000,000: << millionth[s];";
+
+    /**
+     * Number with closest fraction.  This example formats a value using
+     * numerals, but shows the fractional part as a ratio (fraction) rather
+     * than a decimal.  The fraction always has a denominator between 2 and 10.
+     */
+    public static final String closestFraction =
+        "%main:\n"
+               // this rule formats the number if it's 1 or more.  It formats
+               // the integral part using a DecimalFormat ("#,##0" puts
+               // thousands separators in the right places) and the fractional
+               // part using %%frac.  If there is no fractional part, it
+               // just shows the integral part.
+        + "    x.0: <#,##0<[ >%%frac>];\n"
+               // this rule formats the number if it's between 0 and 1.  It
+               // shows only the fractional part (0.5 shows up as "1/2," not
+               // "0 1/2")
+        + "    0.x: >%%frac>;\n"
+        // the fraction rule set.  This works the same way as the one in the
+        // preceding example: We multiply the fractional part of the number
+        // being formatted by each rule's base value and use the rule that
+        // produces the result closest to 0 (or the first rule that produces 0).
+        // Since we only provide rules for the numbers from 2 to 10, we know
+        // we'll get a fraction with a denominator between 2 and 10.
+        // "<0<" causes the numerator of the fraction to be formatted
+        // using numerals
+        + "%%frac:\n"
+        + "    2: 1/2;\n"
+        + "    3: <0</3;\n"
+        + "    4: <0</4;\n"
+        + "    5: <0</5;\n"
+        + "    6: <0</6;\n"
+        + "    7: <0</7;\n"
+        + "    8: <0</8;\n"
+        + "    9: <0</9;\n"
+        + "    10: <0</10;\n";
+
+    /**
+     * American stock-price formatting.  Non-integral stock prices are still
+     * generally shown in eighths or sixteenths of dollars instead of dollars
+     * and cents.  This example formats stock prices in this way if possible,
+     * and in dollars and cents if not.
+     */
+    public static final String stock =
+        "%main:\n"
+               // this rule formats the integral part of the number in numerals
+               // and (if necessary) the fractional part using %%frac1
+        + "    x.0: <#,##0<[>%%frac1>];\n"
+               // this rule is used for values between 0 and 1 and omits the
+               // integral part
+        + "    0.x: >%%frac2>;\n"
+        // this rule set is used to format the fractional part of the number when
+        // there's an integral part before it (again, we try all denominators
+        // and use the "best" one)
+        + "%%frac1:\n"
+               // for even multiples of 1/4, format the fraction using the
+               // typographer's fractions
+        + "    4: <%%quarters<;\n"
+               // format the value as a number of eighths, sixteenths, or
+               // thirty-seconds, whichever produces the most accurate value.
+               // The apostrophe at the front of these rules is ignored, but
+               // it makes the space that follows it significant.  This puts a
+               // space between the value's integral and fractional parts so
+               // you can read it
+        + "    8: ' <0</8;\n"
+        + "    16: ' <0</16;\n"
+        + "    32: ' <0</32;\n"
+               // if we can't reasonably format the number in powers of 2,
+               // then show it as dollars and cents
+        + "    100: .<00<;\n"
+        // this rule set is used when the fractional part of the value stands
+        // alone
+        + "%%frac2:\n"
+        + "    4: <%%quarters<;\n"
+               // for fractions that we can't show using typographer's fractions,
+               // we don't have to put a space before the fraction
+        + "    8: <0</8;\n"
+        + "    16: <0</16;\n"
+        + "    32: <0</32;\n"
+               // but dollars and cents look better with a leading 0
+        + "    100: 0.<00<;\n"
+        // this rule set formats 1/4, 1/2, and 3/4 using typographer's fractions
+        + "%%quarters:\n"
+        + "    ; \u00bc; \u00bd; \u00be;\n"
+        // there are the lenient-parse rules.  These allow the user to type
+        // "1/4," "1/2," and "3/4" instead of their typographical counterparts
+        // and still have them be understood by the formatter
+        + "%%lenient-parse:\n"
+        + "    & '1/4' , \u00bc\n"
+        + "    & '1/2' , \u00bd\n"
+        + "    & '3/4' , \u00be\n;";
+
+    //========================================================================
+    // Changing dimensions
+    //
+    // The next few examples demonstrate using a RuleBasedNumberFormat to
+    // change the units a value is denominated in depending on its magnitude
+    //========================================================================
+
+    /**
+     * The example shows large numbers the way they often appear is nwespapers:
+     * 1,200,000 is formatted as "1.2 million".
+     */
+    public static final String abbEnglish =
+        "=#,##0=;\n"
+        // this is fairly self-explanatory, but note that the << substitution
+        // can show the fractional part of the substitution value if the user
+        // wants it
+        + "1,000,000: <##0.###< million;\n"
+        + "1,000,000,000: <##0.###< billion;\n"
+        + "1,000,000,000,000: <##0.###< trillion;\n";
+
+    /**
+     * This example takes a number of meters and formats it in whatever unit
+     * will produce a number with from one to three digits before the decimal
+     * point.  For example, 230,000 is formatted as "230 km".
+     */
+    public static final String units =
+        "%main:\n"
+               // for values between 0 and 1, delegate to %%small
+        + "    0.x: >%%small>;\n"
+               // otherwise, show between 3 and 6 significant digits of the value
+               // along with the most appropriate unit
+        + "    0: =##0.###= m;\n"
+        + "    1,000: <##0.###< km;\n"
+        + "    1,000,000: <##0.###< Mm;\n"
+        + "    1,000,000,000: <##0.###< Gm;\n"
+        + "    1,000,000,000,000: <#,##0.###< Tm;\n"
+        // %%small formats the number when it's less then 1.  It multiplies the
+        // value by one billion, and then uses %%small2 to actually do the
+        // formatting.
+        + "%%small:\n"
+        + "    1,000,000,000,000: <%%small2<;\n"
+        // this rule set actually formats small values.  %%small passes this
+        // rule set a number of picometers, and it takes care of scaling up as
+        // appropriate in exactly the same way %main does (we can't normally
+        // handle fractional values this way: here, we're concerned about
+        // magnitude; most of the time, we're concerned about precsion)
+        + "%%small2:\n"
+        + "    0: =##0= pm;\n"
+        + "    1,000: <##0.###< nm;\n"
+        + "    1,000,000: <##0.###< \u00b5m;\n"
+        + "    1,000,000,000: <##0.###< mm;\n";
+
+    /**
+     * A more complicated message-formatting example.  Here, in addition to
+     * handling the singular and plural versions of the word, the value is
+     * denominated in bytes, kilobytes, or megabytes depending on its magnitude.
+     * Also notice that it correctly treats a kilobyte as 1,024 bytes (not 1,000),
+     * and a megabyte as 1,024 kilobytes (not 1,000).
+     */
+    public static final String message2 =
+        // this rule supplies the shell of the sentence
+        "x.0: There << free space on the disk.;\n"
+        // handle singular and plural forms of "byte" (and format 0 as
+        // "There is no free space...")
+        + "0: is no;\n"
+        + "is one byte of;\n"
+        + "are =0= bytes of;\n"
+        // for values above 1,024, format the number in K (since "K" is usually
+        // promounced "K" regardless of whether it's singular or plural, we
+        // don't worry about the plural form).  The "/1024" here causes us to
+        // treat a K as 1,024 bytes rather than 1,000 bytes.
+        + "1024/1024: is <0<K of;\n"
+        // for values about 1,048,576, format the number in Mb.  Since "Mb" is
+        // usually promounced "meg" in singular and "megs" in plural, we do have
+        // both singular and plural forms.  Again, notice we treat a megabyte
+        // as 1,024 kilobytes.
+        + "1,048,576/1024: is 1 Mb of;\n"
+        + "2,097,152/1024: are <0< Mb of;";
+
+    //========================================================================
+    // Alternate radices
+    //========================================================================
+
+    /**
+     * This example formats a number in dozens and gross.  This is intended to
+     * demonstrate how this rule set can be used to format numbers in systems
+     * other than base 10.  The "/12" after the rules' base values controls this.
+     * Also notice that the base doesn't have to be consistent throughout the
+     * whole rule set: we go back to base 10 for values over 1,000.
+     */
+    public static final String dozens =
+        // words for numbers...
+        "zero; one; two; three; four; five; six;\n"
+        + "seven; eight; nine; ten; eleven;\n"
+        // format values over 12 in dozens
+        + "12/12: << dozen[ and >>];\n"
+        // format values over 144 in gross
+        + "144/12: << gross[, >>];\n"
+        // format values over 1,000 in thousands
+        + "1000: << thousand[, >>];\n"
+        // overflow rule.  Format values over 10,000 in numerals
+        + "10,000: =#,##0=;\n";
+
+    //========================================================================
+    // Major and minor units
+    //
+    // These examples show how a single value can be divided up into major
+    // and minor units that don't relate to each other by a factor of 10.
+    //========================================================================
+
+    /**
+     * This example formats a number of seconds in sexagesimal notation
+     * (i.e., hours, minutes, and seconds).  %with-words formats it with
+     * words (3740 is "1 hour, 2 minutes, 20 seconds") and %in-numerals
+     * formats it entirely in numerals (3740 is "1:02:20").
+     */
+    public static final String durationInSeconds =
+        // main rule set for formatting with words
+        "%with-words:\n"
+               // take care of singular and plural forms of "second"
+        + "    0 seconds; 1 second; =0= seconds;\n"
+               // use %%min to format values greater than 60 seconds
+        + "    60/60: <%%min<[, >>];\n"
+               // use %%hr to format values greater than 3,600 seconds
+               // (the ">>>" below causes us to see the number of minutes
+               // when when there are zero minutes)
+        + "    3600/60: <%%hr<[, >>>];\n"
+        // this rule set takes care of the singular and plural forms
+        // of "minute"
+        + "%%min:\n"
+        + "    0 minutes; 1 minute; =0= minutes;\n"
+        // this rule set takes care of the singular and plural forms
+        // of "hour"
+        + "%%hr:\n"
+        + "    0 hours; 1 hour; =0= hours;\n"
+
+        // main rule set for formatting in numerals
+        + "%in-numerals:\n"
+               // values below 60 seconds are shown with "sec."
+        + "    =0= sec.;\n"
+               // higher values are shown with colons: %%min-sec is used for
+               // values below 3,600 seconds...
+        + "    60: =%%min-sec=;\n"
+               // ...and %%hr-min-sec is used for values of 3,600 seconds
+               // and above
+        + "    3600: =%%hr-min-sec=;\n"
+        // this rule causes values of less than 10 minutes to show without
+        // a leading zero
+        + "%%min-sec:\n"
+        + "    0: :=00=;\n"
+        + "    60/60: <0<>>;\n"
+        // this rule set is used for values of 3,600 or more.  Minutes are always
+        // shown, and always shown with two digits
+        + "%%hr-min-sec:\n"
+        + "    0: :=00=;\n"
+        + "    60/60: <00<>>;\n"
+        + "    3600/60: <#,##0<:>>>;\n"
+        // the lenient-parse rules allow several different characters to be used
+        // as delimiters between hours, minutes, and seconds
+        + "%%lenient-parse:\n"
+        + "    & : = . = ' ' = -;\n";
+
+    /**
+     * This example formats a number of hours in sexagesimal notation (i.e.,
+     * hours, minutes, and seconds).  %with-words formats the value using
+     * words for the units, and %in-numerals formats the value using only
+     * numerals.
+     */
+    public static final String durationInHours =
+        // main entry point for formatting with words
+        "%with-words:\n"
+               // this rule omits minutes and seconds when the value is
+               // an even number of hours
+        + "    x.0: <<[, >%%min-sec>];\n"
+               // these rules take care of the singular and plural forms
+               // of hours
+        + "    0 hours; 1 hour; =#,##0= hours;\n"
+        // this rule set takes the fractional part of the number and multiplies
+        // it by 3,600 (turning it into a number of seconds).  Then it delegates
+        // to %%min-sec-implementation to format the resulting value
+        + "%%min-sec:\n"
+        + "    3600: =%%min-sec-implementation=;\n"
+        // this rule set formats the seconds as either seconds or minutes and
+        // seconds, and takes care of the singular and plural forms of
+        // "minute" and "second"
+        + "%%min-sec-implementation:\n"
+        + "    0 seconds; 1 second; =0= seconds;\n"
+        + "    60/60: 1 minute[, >>];\n"
+        + "    120/60: <0< minutes[, >>];\n"
+
+        // main entry point for formatting in numerals
+        + "%in-numerals:\n"
+               // show minutes even for even numbers of hours
+        + "    x.0: <#,##0<:00;\n"
+               // delegate to %%min-sec2 to format minutes and seconds
+        + "    x.x: <#,##0<:>%%min-sec2>;\n"
+        // this rule set formats minutes when there is an even number of
+        // minutes, and delegates to %%min-sec2-implementation when there
+        // are seconds
+        + "%%min-sec2:\n"
+        + "    60: <00<;\n"
+        + "    3600: <%%min-sec2-implementation<;\n"
+        // these two rule sets are used to format the minutes and seconds
+        + "%%min-sec2-implementation:\n"
+               // if there are fewer than 60 seconds, show the minutes anyway
+        + "    0: 00:=00=;\n"
+               // if there are minutes, format them too, and always use 2 digits
+               // for both minutes and seconds
+        + "    60: =%%min-sec3=;\n"
+        + "%%min-sec3:\n"
+        + "    0: :=00=;\n"
+        + "    60/60: <00<>>;\n"
+        // the lenient-parse rules allow the user to use any of several
+        // characters as delimiters between hours, minutes, and seconds
+        + "%%lenient-parse:\n"
+        + "    & : = . = ' ' = -;\n";
+
+    /**
+     * This rule set formats a number of pounds as pounds, shillings, and
+     * pence in the old English system of currency.
+     */
+    public static final String poundsShillingsAndPence =
+        // for values of 1 or more, format the integral part with a pound
+        // sign in front, and show shillings and pence if necessary
+        "%main:\n"
+        + "    x.0: \u00a3<#,##0<[ >%%shillings-and-pence>];\n"
+        // for values between 0 and 1, omit the number of pounds
+        + "    0.x: >%%pence-alone>;\n"
+        // this rule set is used to show shillings and pence.  It multiplies
+        // the fractional part of the number by 240 (the number of pence in a
+        // pound) and uses %%shillings-and-pence-implementation to format
+        // the result
+        + "%%shillings-and-pence:\n"
+        + "    240: <%%shillings-and-pence-implementation<;\n"
+        // this rule set is used to show shillings and pence when there are
+        // no pounds.  It also multiplies the value by 240, and then it uses
+        // %%pence-alone-implementation to format the result.
+        + "%%pence-alone:\n"
+        + "    240: <%%pence-alone-implementation<;\n"
+        // this rule set formats a number of pence when we know we also
+        // have pounds.  We always show shillings (with a 0 if necessary),
+        // but only show pence if the value isn't an even number of shillings
+        + "%%shillings-and-pence-implementation:\n"
+        + "    0/; 0/=0=;\n"
+        + "    12/12: <0</[>0>];\n"
+        // this rule set formats a number of pence when we know there are
+        // no pounds.  Values less than a shilling are shown with "d." (the
+        // abbreviation for pence), and values greater than a shilling are
+        // shown with a shilling bar (and without pence when the value is
+        // an even number of shillings)
+        + "%%pence-alone-implementation:\n"
+        + "    =0= d.;\n"
+        + "    12/12: <0</[>0>];\n";
+
+    //========================================================================
+    // Alternate numeration systems
+    //
+    // These examples show how RuleBasedNumberFormat can be used to format
+    // numbers using non-positional numeration systems.
+    //========================================================================
+
+    /**
+     * Arabic digits.  This example formats numbers in Arabic numerals.
+     * Normally, you'd do this with DecimalFormat, but this shows that
+     * RuleBasedNumberFormat can handle it too.
+     */
+    public static final String arabicNumerals =
+        "0; 1; 2; 3; 4; 5; 6; 7; 8; 9;\n"
+        + "10: <<>>;\n"
+        + "100: <<>>>;\n"
+        + "1000: <<,>>>;\n"
+        + "1,000,000: <<,>>>;\n"
+        + "1,000,000,000: <<,>>>;\n"
+        + "1,000,000,000,000: <<,>>>;\n"
+        + "1,000,000,000,000,000: =#,##0=;\n"
+        + "-x: ->>;\n"
+        + "x.x: <<.>>;";
+
+    /**
+     * Words for digits.  Follows the same pattern as the Arabic-numerals
+     * example above, but uses words for the various digits (e.g., 123 comes
+     * out as "one two three").
+     */
+    public static final String wordsForDigits =
+        "-x: minus >>;\n"
+        + "x.x: << point >>;\n"
+        + "zero; one; two; three; four; five; six;\n"
+        + "    seven; eight; nine;\n"
+        + "10: << >>;\n"
+        + "100: << >>>;\n"
+        + "1000: <<, >>>;\n"
+        + "1,000,000: <<, >>>;\n"
+        + "1,000,000,000: <<, >>>;\n"
+        + "1,000,000,000,000: <<, >>>;\n"
+        + "1,000,000,000,000,000: =#,##0=;\n";
+
+    /**
+     * This example formats numbers using Chinese characters in the Arabic
+     * place-value method.  This was used historically in China for a while.
+     */
+    public static final String chinesePlaceValue =
+        "\u3007; \u4e00; \u4e8c; \u4e09; \u56db; \u4e94; \u516d; \u4e03; \u516b; \u4e5d;\n"
+        + "10: <<>>;\n"
+        + "100: <<>>>;\n"
+        + "1000: <<>>>;\n"
+        + "1,000,000: <<>>>;\n"
+        + "1,000,000,000: <<>>>;\n"
+        + "1,000,000,000,000: <<>>>;\n"
+        + "1,000,000,000,000,000: =#,##0=;\n";
+
+    /**
+     * Roman numerals.  This example has two variants: %modern shows how large
+     * numbers are usually handled today; %historical ses the older symbols for
+     * thousands.
+     */
+    public static final String romanNumerals =
+        "%historical:\n"
+        + "    =%modern=;\n"
+               // in early Roman numerals, 1,000 was shown with a circle
+               // bisected by a vertical line.  Additional thousands were
+               // shown by adding more concentric circles, and fives were
+               // shown by cutting the symbol for next-higher power of 10
+               // in half (the letter D for 500 evolved from this).
+               // We could go beyond 40,000, but Unicode doesn't encode
+               // the symbols for higher numbers/
+        + "    1000: \u2180[>>]; 2000: \u2180\u2180[>>]; 3000: \u2180\u2180\u2180[>>]; 4000: \u2180\u2181[>>];\n"
+        + "    5000: \u2181[>>]; 6000: \u2181\u2180[>>]; 7000: \u2181\u2180\u2180[>>];\n"
+        + "    8000: \u2181\u2180\u2180\u2180[>>]; 9000: \u2180\u2182[>>];\n"
+        + "    10,000: \u2182[>>]; 20,000: \u2182\u2182[>>]; 30,000: \u2182\u2182\u2182[>>];\n"
+        + "    40,000: =#,##0=;\n"
+        + "%modern:\n"
+        + "    ; I; II; III; IV; V; VI; VII; VIII; IX;\n"
+        + "    10: X[>>]; 20: XX[>>]; 30: XXX[>>]; 40: XL[>>]; 50: L[>>];\n"
+        + "    60: LX[>>]; 70: LXX[>>]; 80: LXXX[>>]; 90: XC[>>];\n"
+        + "    100: C[>>]; 200: CC[>>]; 300: CCC[>>]; 400: CD[>>]; 500: D[>>];\n"
+        + "    600: DC[>>]; 700: DCC[>>]; 800: DCCC[>>]; 900: CM[>>];\n"
+               // in modern Roman numerals, high numbers are generally shown
+               // by placing a bar over the letters for the lower numbers:
+               // the bar multiplied a letter's value by 1,000
+        + "    1000: M[>>]; 2000: MM[>>]; 3000: MMM[>>]; 4000: MV\u0306[>>];\n"
+        + "    5000: V\u0306[>>]; 6000: V\u0306M[>>]; 7000: V\u0306MM[>>];\n"
+        + "    8000: V\u0306MMM[>>]; 9000: MX\u0306[>>];\n"
+        + "    10,000: X\u0306[>>]; 20,000: X\u0306X\u0306[>>]; 30,000: X\u0306X\u0306X\u0306[>>];\n"
+        + "    40,000: X\u0306L\u0306[>>]; 50,000: L\u0306[>>]; 60,000: L\u0306X\u0306[>>];\n"
+        + "    70,000: L\u0306X\u0306X\u0306[>>]; 80,000: L\u0306X\u0306X\u0306X\u0306[>>];\n"
+        + "    90,000: X\u0306C\u0306[>>];\n"
+        + "    100,000: C\u0306[>>]; 200,000: C\u0306C\u0306[>>]; 300,000: C\u0306C\u0306[>>];\n"
+        + "    400,000: C\u0306D\u0306[>>]; 500,000: D\u0306[>>]; 600,000: D\u0306C\u0306[>>];\n"
+        + "    700,000: D\u0306C\u0306C\u0306[>>]; 800,000: D\u0306C\u0306C\u0306C\u0306[>>];\n"
+        + "    900,000: =#,##0=;\n";
+
+    /**
+     * Hebrew alphabetic numerals.  Before adoption of Arabic numerals, Hebrew speakers
+     * used the letter of their alphabet as numerals.  The first nine letters of
+     * the alphabet repesented the values from 1 to 9, the second nine letters the
+     * multiples of 10, and the remaining letters the multiples of 100.  Since they
+     * ran out of letters at 400, the remaining multiples of 100 were represented
+     * using combinations of the existing letters for the hundreds.  Numbers were
+     * distinguished from words in a number of different ways: the way shown here
+     * uses a single mark after a number consisting of one letter, and a double
+     * mark between the last two letters of a number consisting of two or more
+     * letters.  Two dots over a letter multiplied its value by 1,000.  Also, since
+     * the letter for 10 is the first letter of God's name and the letters for 5 and 6
+     * are letters in God's name, which wasn't supposed to be written or spoken, 15 and
+     * 16 were usually written as 9 + 6 and 9 + 7 instead of 10 + 5 and 10 + 6.
+     */
+    public static final String hebrewAlphabetic =
+        // letters for the ones
+        "%%ones:\n"
+        + "    (no zero); \u05d0; \u05d1; \u05d2; \u05d3; \u05d4; \u05d5; \u05d6; \u05d7; \u05d8;\n"
+        // letters for the tens
+        + "%%tens:\n"
+        + "    ; \u05d9; \u05db; \u05dc; \u05de; \u05e0; \u05e1; \u05e2; \u05e4; \u05e6;\n"
+        // letters for the first four hundreds
+        + "%%hundreds:\n"
+        + "    ; \u05e7; \u05e8; \u05e9; \u05ea;\n"
+        // this rule set is used to write the combination of the tens and ones digits
+        // when we know that no other digits precede them: they put the numeral marks
+        // in the right place and properly handle 15 and 16 (I'm using the mathematical
+        // prime characters for the numeral marks because my Unicode font doesn't
+        // include the real Hebrew characters, which look just like the prime marks)
+        + "%%tens-and-ones:\n"
+               // for values less than 10, just use %%ones and put the numeral mark
+               // afterward
+        + "    =%%ones=\u2032;\n"
+               // put the numeral mark at the end for 10, but in the middle for
+               // 11 through 14
+        + "    10: <%%tens<\u2032; <%%tens<\u2033>%%ones>;\n"
+               // special-case 15 and 16
+        + "    15: \u05d8\u2033\u05d5; 16: \u05d8\u2033\u05d6;\n"
+               // go back to the normal method at 17
+        + "    17: <%%tens<\u2033>%%ones>;\n"
+               // repeat the rules for 10 and 11 to cover the values from 20 to 99
+        + "    20: <%%tens<\u2032; <%%tens<\u2033>%%ones>;\n"
+        // this rule set is used to format numbers below 1,000.  It relies on
+        // %%tens-and-ones to format the tens and ones places, and adds logic
+        // to handle the high hundreds and the numeral marks when there is no
+        // tens digit.  Notice how the rules are paired: all of these pairs of
+        // rules take advantage of the rollback rule: if the value (between 100
+        // and 499) is an even multiple of 100, the rule for 100 is used; otherwise,
+        // the rule for 101 (the following rule) is used.  The first rule in each
+        // pair (the one for the even multiple) places the numeral mark in a different
+        // spot than the second rule in each pair (which knows there are more digits
+        // and relies on the rule supplying them to also supply the numeral mark).
+        // The call to %%null in line 10 is there simply to invoke the rollback
+        // rule.
+        + "%%low-order:\n"
+               // this rule is only called when there are other characters before.
+               // It places the numeral mark before the last digit
+        + "    \u2033=%%ones=;\n"
+               // the rule for 10 places the numeral mark before the 10 character
+               // (because we know it's the last character); the rule for 11 relies
+               // on %%tens-and-ones to place the numeral mark
+        + "    10: \u2033<%%tens<; =%%tens-and-ones=>%%null>;\n"
+               // the rule for 100 places the numeral mark before the 100 character
+               // (we know it's the last character); the rule for 101 recurses to
+               // fill in the remaining digits and the numeral mark
+        + "    100: <%%hundreds<\u2032; <%%hundreds<>>;\n"
+               // special-case the hundreds from 500 to 900 because they consist of
+               // more than one character
+        + "    500: \u05ea\u2033\u05e7; \u05ea\u05e7>>;\n"
+        + "    600: \u05ea\u2033\u05e8; \u05ea\u05e8>>;\n"
+        + "    700: \u05ea\u2033\u05e9; \u05ea\u05e9>>;\n"
+        + "    800: \u05ea\u2033\u05ea; \u05ea\u05ea>>;\n"
+        + "    900: \u05ea\u05ea\u2033\u05e7; \u05ea\u05ea\u05e7>>;\n"
+        // this rule set is used to format values of 1,000 or more.  Here, we don't
+        // worry about the numeral mark, and we add two dots (the Unicode combining
+        // diaeresis character) to ever letter
+        + "%%high-order:\n"
+               // put the ones digit, followed by the diaeresis
+        + "    =%%ones=\u0308;\n"
+               // the tens can be handled with recursion
+        + "    10: <%%tens<\u0308[>>];\n"
+               // still have to special-case 15 and 16
+        + "    15: \u05d8\u0308\u05d5\u0308; 16: \u05d8\u003078\u05d6\u0308;\n"
+               // back to the regular rules at 17
+        + "    17: <%%tens<\u0308[>>];\n"
+               // the hundreds with the dots added (and without worrying about
+               // placing the numeral mark)
+        + "    100: <%%hundreds<\u0308[>>];\n"
+        + "    500: \u05ea\u0308\u05e7\u0308[>>];\n"
+        + "    600: \u05ea\u0308\u05e8\u0308[>>];\n"
+        + "    700: \u05ea\u0308\u05e9\u0308[>>];\n"
+        + "    800: \u05ea\u0308\u05ea\u0308[>>];\n"
+        + "    900: \u05ea\u0308\u05ea\u0308\u05e7\u0308[>>];\n"
+        // this rule set doesn't do anything; it's used by some other rules to
+        // invoke the rollback rule
+        + " %%null:\n"
+        + "    ;\n"
+        // the main rule set.
+        + "%main:\n"
+               // for values below 10, just output the letter and the numeral mark
+        + "    =%%ones=\u2032;\n"
+               // for values from 10 to 99, use %%tens-and-ones to do the formatting
+        + "    10: =%%tens-and-ones=;\n"
+               // for values from 100 to 999, use %%low-order to do the formatting
+        + "    100: =%%low-order=;\n"
+               // for values of 1,000 and over, use %%high-order to do the formatting
+        + "    1000: <%%high-order<[>%%low-order>];\n";
+
+    /**
+     * Greek alphabetic numerals.  The Greeks, before adopting the Arabic numerals,
+     * also used the letters of their alphabet as numerals.  There are three now-
+     * obsolete Greek letters that are used as numerals; many fonts don't have them.
+     * Large numbers were handled many different ways; the way shown here divides
+     * large numbers into groups of four letters (factors of 10,000), and separates
+     * the groups with the capital letter mu (for myriad).  Capital letters are used
+     * for values below 10,000; small letters for higher numbers (to make the capital
+     * mu stand out).
+     */
+    public static final String greekAlphabetic =
+        // this rule set is used for formatting numbers below 10,000.  It uses
+        // capital letters.
+        "%%low-order:\n"
+        + "    (no zero); \u0391; \u0392; \u0393; \u0394; \u0395; \u03dc; \u0396; \u0397; \u0398;\n"
+        + "    10: \u0399[>>]; 20: \u039a[>>]; 30: \u039b[>>]; 40: \u039c[>>]; 50: \u039d[>>];\n"
+        + "    60: \u039e[>>]; 70: \u039f[>>]; 80: \u03a0[>>]; 90: \u03de[>>];\n"
+        + "    100: \u03a1[>>]; 200: \u03a3[>>]; 300: \u03a4[>>]; 400: \u03a5[>>];\n"
+        + "    500: \u03a6[>>]; 600: \u03a7[>>]; 700: \u03a8[>>]; 800: \u03a9[>>];\n"
+        + "    900: \u03e0[>>];\n"
+               // the thousands are represented by the same numbers as the ones, but
+               // with a comma-like mark added to their left shoulder
+        + "    1000: \u0391\u0313[>>]; 2000: \u0392\u0313[>>]; 3000: \u0393\u0313[>>];\n"
+        + "    4000: \u0394\u0313[>>]; 5000: \u0395\u0313[>>]; 6000: \u03dc\u0313[>>];\n"
+        + "    7000: \u0396\u0313[>>]; 8000: \u0397\u0313[>>]; 9000: \u0398\u0313[>>];\n"
+        // this rule set is the same as above, but uses lowercase letters.  It is used
+        // for formatting the groups in numbers above 10,000.
+        + "%%high-order:\n"
+        + "    (no zero); \u03b1; \u03b2; \u03b3; \u03b4; \u03b5; \u03dc; \u03b6; \u03b7; \u03b8;\n"
+        + "    10: \u03b9[>>]; 20: \u03ba[>>]; 30: \u03bb[>>]; 40: \u03bc[>>]; 50: \u03bd[>>];\n"
+        + "    60: \u03be[>>]; 70: \u03bf[>>]; 80: \u03c0[>>]; 90: \u03de[>>];\n"
+        + "    100: \u03c1[>>]; 200: \u03c3[>>]; 300: \u03c4[>>]; 400: \u03c5[>>];\n"
+        + "    500: \u03c6[>>]; 600: \u03c7[>>]; 700: \u03c8[>>]; 800: \u03c9[>>];\n"
+        + "    900: \u03c0[>>];\n"
+        + "    1000: \u03b1\u0313[>>]; 2000: \u03b2\u0313[>>]; 3000: \u03b3\u0313[>>];\n"
+        + "    4000: \u03b4\u0313[>>]; 5000: \u03b5\u0313[>>]; 6000: \u03dc\u0313[>>];\n"
+        + "    7000: \u03b6\u0313[>>]; 8000: \u03b7\u0313[>>]; 9000: \u03b8\u0313[>>];\n"
+        // the main rule set
+        + "%main:\n"
+               // for values below 10,000, just use %%low-order
+        + "    =%%low-order=;\n"
+               // for values above 10,000, split into two groups of four digits
+               // and format each with %%high-order (putting an M in betwen)
+        + "    10,000: <%%high-order<\u039c>%%high-order>;\n"
+               // for values above 100,000,000, add another group onto the front
+               // and another M
+        + "    100,000,000: <%%high-order<\u039c>>\n";
+
+    /**
+     * A list of all the sample rule sets, used by the demo program.
+     */
+    public static final String[] sampleRuleSets =
+        { usEnglish,
+          ukEnglish,
+          spanish,
+          french,
+          swissFrench,
+          german,
+          italian,
+          swedish,
+          dutch,
+          japanese,
+          greek,
+          russian,
+          hebrew,
+          ordinal,
+          message1,
+          dollarsAndCents,
+          decimalAsFraction,
+          closestFraction,
+          stock,
+          abbEnglish,
+          units,
+          message2,
+          dozens,
+          durationInSeconds,
+          durationInHours,
+          poundsShillingsAndPence,
+          arabicNumerals,
+          wordsForDigits,
+          chinesePlaceValue,
+          romanNumerals,
+          hebrewAlphabetic,
+          greekAlphabetic };
+
+    /**
+     * The displayable names for all the sample rule sets, in the same order as
+     * the preceding array.
+     */
+    public static final String[] sampleRuleSetNames =
+        { "English (US)",
+          "English (UK)",
+          "Spanish",
+          "French (France)",
+          "French (Switzerland)",
+          "German",
+          "Italian",
+          "Swedish",
+          "Dutch",
+          "Japanese",
+          "Greek",
+          "Russian",
+          "Hebrew",
+          "English ordinal abbreviations",
+          "Simple message formatting",
+          "Dollars and cents",
+          "Decimals as fractions",
+          "Closest fraction",
+          "Stock prices",
+          "Abbreviated US English",
+          "Changing dimensions",
+          "Complex message formatting",
+          "Dozens",
+          "Duration (value in seconds)",
+          "Duration (value in hours)",
+          "Pounds, shillings, and pence",
+          "Arabic numerals",
+          "Words for digits",
+          "Chinese place-value notation",
+          "Roman numerals",
+          "Hebrew ahlphabetic numerals",
+          "Greek alphabetic numerals" };
+
+    /**
+     * The base locale for each of the sample rule sets.  The locale is used to
+     * determine DecimalFormat behavior, lenient-parse behavior, and text-display
+     * selection (we have a hack in here to allow display of non-Latin scripts).
+     * Null means the locale setting is irrelevant and the default can be used.
+     */
+    public static final Locale[] sampleRuleSetLocales =
+        { Locale.US,
+          Locale.UK,
+          new Locale("es", "", ""),
+          Locale.FRANCE,
+          new Locale("fr", "CH", ""),
+          Locale.GERMAN,
+          Locale.ITALIAN,
+          new Locale("sv", "", ""),
+          new Locale("nl", "", ""),
+          Locale.JAPANESE,
+          new Locale("el", "", ""),
+          new Locale("ru", "", ""),
+          new Locale("iw", "", ""),
+          Locale.ENGLISH,
+          Locale.ENGLISH,
+          Locale.US,
+          Locale.ENGLISH,
+          null,
+          null,
+          Locale.ENGLISH,
+          null,
+          Locale.ENGLISH,
+          Locale.ENGLISH,
+          null,
+          null,
+          Locale.UK,
+          null,
+          Locale.ENGLISH,
+          new Locale("zh", "", ""),
+          null,
+          new Locale("iw", "", ""),
+          new Locale("el", "", ""),
+          null };
+
+        public static final String[] sampleRuleSetCommentary = {
+            "This demonstration version of the "
+            + "U.S. English spellout rules has four variants: 1) %simplified is a "
+            + "set of rules showing the simple method of spelling out numbers in "
+            + "English: 289 is formatted as \"two hundred eighty-nine\".  2) %alt-teens "
+            + "is the same as %simplified, except that values between 1,000 and 9,999 "
+            + "whose hundreds place isn't zero are formatted in hundreds.  For example, "
+            + "1,983 is formatted as \"nineteen hundred eighty-three,\" and 2,183 is "
+            + "formatted as \"twenty-one hundred eighty-three,\" but 2,083 is still "
+            + "formatted as \"two thousand eighty-three.\"  3) %ordinal formats the "
+            + "values as ordinal numbers in English (e.g., 289 is \"two hundred eighty-"
+            + "ninth\").  4) %default uses a more complicated algorithm to format "
+            + "numbers in a more natural way: 289 is formatted as \"two hundred AND "
+            + "eighty-nine\" and commas are inserted between the thousands groups for "
+            + "values above 100,000.",
+
+            "U.K. English has one significant "
+            + "difference from U.S. English: the names for values of 1,000,000,000 "
+            + "and higher.  In American English, each successive \"-illion\" is 1,000 "
+            + "times greater than the preceding one: 1,000,000,000 is \"one billion\" "
+            + "and 1,000,000,000,000 is \"one trillion.\"  In British English, each "
+            + "successive \"-illion\" is one million times greater than the one before: "
+            + "\"one billion\" is 1,000,000,000,000 (or what Americans would call a "
+            + "\"trillion\"), and \"one trillion\" is 1,000,000,000,000,000,000.  "
+            + "1,000,000,000 in British English is \"one thousand million.\"  (This "
+            + "value is sometimes called a \"milliard,\" but this word seems to have "
+            + "fallen into disuse.)",
+
+            "The Spanish rules are quite similar to "
+            + "the English rules, but there are some important differences: "
+            + "First, we have to provide separate rules for most of the twenties "
+            + "because the ones digit frequently picks up an accent mark that it "
+            + "doesn't have when standing alone.  Second, each multiple of 100 has "
+            + "to be specified separately because the multiplier on 100 very often "
+            + "changes form in the contraction: 500 is \"quinientos,\" not "
+            + "\"cincocientos.\"  In addition, the word for 100 is \"cien\" when "
+            + "standing alone, but changes to \"ciento\" when followed by more digits.  "
+            + "There also some other differences.",
+
+            "French adds some interesting quirks of its "
+            + "own: 1) The word \"et\" is interposed between the tens and ones digits, "
+            + "but only if the ones digit if 1: 20 is \"vingt,\" and 2 is \"vingt-deux,\" "
+            + "but 21 is \"vingt-et-un.\"  2)  There are no words for 70, 80, or 90.  "
+            + "\"quatre-vingts\" (\"four twenties\") is used for 80, and values proceed "
+            + "by score from 60 to 99 (e.g., 73 is \"soixante-treize\" [\"sixty-thirteen\"]).  "
+            + "Numbers from 1,100 to 1,199 are rendered as hundreds rather than "
+            + "thousands: 1,100 is \"onze cents\" (\"eleven hundred\"), rather than "
+            + "\"mille cent\" (\"one thousand one hundred\")",
+
+            "Swiss French differs from French French "
+            + "in that it does have words for 70, 80, and 90.  This rule set shows them, "
+            + "and is simpler as a result.",
+
+            "German also adds some interesting "
+            + "characteristics.  For values below 1,000,000, numbers are customarily "
+            + "written out as a single word.  And the ones digit PRECEDES the tens "
+            + "digit (e.g., 23 is \"dreiundzwanzig,\" not \"zwanzigunddrei\").",
+
+            "Like German, most Italian numbers are "
+            + "written as single words.  What makes these rules complicated is the rule "
+            + "that says that when a word ending in a vowel and a word beginning with "
+            + "a vowel are combined into a compound, the vowel is dropped from the "
+            + "end of the first word: 180 is \"centottanta,\" not \"centoottanta.\"  "
+            + "The complexity of this rule set is to produce this behavior.",
+
+            "Spellout rules for Swedish.",
+
+            "Spellout rules for Dutch.  Notice that in Dutch, as in German,"
+            + "the ones digit precedes the tens digit.",
+
+            "In Japanese, there really isn't any "
+            + "distinction between a number written out in digits and a number "
+            + "written out in words: the ideographic characters are both digits "
+            + "and words.  This rule set provides two variants:  %traditional "
+            + "uses the traditional CJK numerals (which are also used in China "
+            + "and Korea).  %financial uses alternate ideographs for many numbers "
+            + "that are harder to alter than the traditional numerals (one could "
+            + "fairly easily change a one to "
+            + "a three just by adding two strokes, for example).  This is also done in "
+            + "the other countries using Chinese idographs, but different ideographs "
+            + "are used in those places.",
+
+            "Again in Greek we have to supply the words "
+            + "for the multiples of 100 because they can't be derived algorithmically.  "
+            + "Also, the tens dgit changes form when followed by a ones digit: an "
+            + "accent mark disappears from the tens digit and moves to the ones digit.  "
+            + "Therefore, instead of using the [] notation, we actually have to use "
+            + "two separate rules for each multiple of 10 to show the two forms of "
+            + "the word.",
+
+            "Spellout rules for Russian.",
+
+            "Spellout rules for Hebrew.  Hebrew actually has inflected forms for "
+            + "most of the lower-order numbers.  The masculine forms are shown "
+            + "here.",
+
+            "This rule set adds an English ordinal abbreviation to the end of a "
+            + "number.  For example, 2 is formatted as \"2nd\".  Parsing doesn't work with "
+            + "this rule set.  To parse, use DecimalFormat on the numeral.",
+
+            "This is a simple message-formatting example.  Normally one would "
+            + "use ChoiceFormat and MessageFormat to do something this simple, "
+            + "but this shows it could be done with RuleBasedNumberFormat too.  "
+            + "A message-formatting example that might work better with "
+            + "RuleBasedNumberFormat appears later.",
+
+            "The next few examples demonstrate fraction handling.  "
+            + "This example formats a number in one of the two styles often used "
+            + "on checks.  %dollars-and-hundredths formats cents as hundredths of "
+            + "a dollar (23.40 comes out as \"twenty-three and 40/100 dollars\").  "
+            + "%dollars-and-cents formats in dollars and cents (23.40 comes out as "
+            + "\"twenty-three dollars and forty cents\")",
+
+            "This rule set shows the fractional part of the number as a fraction "
+            + "with a power of 10 as the denominator.  Some languages don't spell "
+            + "out the fractional part of a number as \"point one two three,\" but "
+            + "always render it as a fraction.  If we still want to treat the fractional "
+            + "part of the number as a decimal, then the fraction's denominator "
+            + "is always a power of 10.  This example does that: 23.125 is formatted "
+            + "as \"twenty-three and one hundred twenty-five thousandths\" (as opposed "
+            + "to \"twenty-three point one two five\" or \"twenty-three and one eighth\").",
+
+            "Number with closest fraction.  This example formats a value using "
+            + "numerals, but shows the fractional part as a ratio (fraction) rather "
+            + "than a decimal.  The fraction always has a denominator between 2 and 10.",
+
+            "American stock-price formatting.  Non-integral stock prices are still "
+            + "generally shown in eighths or sixteenths of dollars instead of dollars "
+            + "and cents.  This example formats stock prices in this way if possible, "
+            + "and in dollars and cents if not.",
+
+            "The next few examples demonstrate using a RuleBasedNumberFormat to "
+            + "change the units a value is denominated in depending on its magnitude.  "
+            + "The example shows large numbers the way they often appear is nwespapers: "
+            + "1,200,000 is formatted as \"1.2 million\".",
+
+            "This example takes a number of meters and formats it in whatever unit "
+            + "will produce a number with from one to three digits before the decimal "
+            + "point.  For example, 230,000 is formatted as \"230 km\".",
+
+            "A more complicated message-formatting example.  Here, in addition to "
+            + "handling the singular and plural versions of the word, the value is "
+            + "denominated in bytes, kilobytes, or megabytes depending on its magnitude.  "
+            + "Also notice that it correctly treats a kilobyte as 1,024 bytes (not 1,000), "
+            + "and a megabyte as 1,024 kilobytes (not 1,000).",
+
+            "This example formats a number in dozens and gross.  This is intended to "
+            + "demonstrate how this rule set can be used to format numbers in systems "
+            + "other than base 10.  The \"/12\" after the rules' base values controls this.  "
+            + "Also notice that the base doesn't have to be consistent throughout the "
+            + "whole rule set: we go back to base 10 for values over 1,000.",
+
+            "The next few examples show how a single value can be divided up into major "
+            + "and minor units that don't relate to each other by a factor of 10.  "
+            + "This example formats a number of seconds in sexagesimal notation "
+            + "(i.e., hours, minutes, and seconds).  %with-words formats it with "
+            + "words (3740 is \"1 hour, 2 minutes, 20 seconds\") and %in-numerals "
+            + "formats it entirely in numerals (3740 is \"1:02:20\").",
+
+            "This example formats a number of hours in sexagesimal notation (i.e., "
+            + "hours, minutes, and seconds).  %with-words formats the value using "
+            + "words for the units, and %in-numerals formats the value using only "
+            + "numerals.",
+
+            "This rule set formats a number of pounds as pounds, shillings, and "
+            + "pence in the old English system of currency.",
+
+            "These examples show how RuleBasedNumberFormat can be used to format "
+            + "numbers using non-positional numeration systems.  "
+            + "This example formats numbers in Arabic numerals.  "
+            + "Normally, you'd do this with DecimalFormat, but this shows that "
+            + "RuleBasedNumberFormat can handle it too.",
+
+            "This example follows the same pattern as the Arabic-numerals "
+            + "example, but uses words for the various digits (e.g., 123 comes "
+            + "out as \"one two three\").",
+
+            "This example formats numbers using Chinese characters in the Arabic "
+            + "place-value method.  This was used historically in China for a while.",
+
+            "Roman numerals.  This example has two variants: %modern shows how large "
+            + "numbers are usually handled today; %historical ses the older symbols for "
+            + "thousands.  Not all of the characters are displayable with most fonts.",
+
+            "Hebrew alphabetic numerals.  Before adoption of Arabic numerals, Hebrew speakers "
+            + "used the letter of their alphabet as numerals.  The first nine letters of "
+            + "the alphabet repesented the values from 1 to 9, the second nine letters the "
+            + "multiples of 10, and the remaining letters the multiples of 100.  Since they "
+            + "ran out of letters at 400, the remaining multiples of 100 were represented "
+            + "using combinations of the existing letters for the hundreds.  Numbers were "
+            + "distinguished from words in a number of different ways: the way shown here "
+            + "uses a single mark after a number consisting of one letter, and a double "
+            + "mark between the last two letters of a number consisting of two or more "
+            + "letters.  Two dots over a letter multiplied its value by 1,000.  Also, since "
+            + "the letter for 10 is the first letter of God's name and the letters for 5 and 6 "
+            + "are letters in God's name, which wasn't supposed to be written or spoken, 15 and "
+            + "16 were usually written as 9 + 6 and 9 + 7 instead of 10 + 5 and 10 + 6.",
+
+            "Greek alphabetic numerals.  The Greeks, before adopting the Arabic numerals, "
+            + "also used the letters of their alphabet as numerals.  There are three now-"
+            + "obsolete Greek letters that are used as numerals; many fonts don't have them.  "
+            + "Large numbers were handled many different ways; the way shown here divides "
+            + "large numbers into groups of four letters (factors of 10,000), and separates "
+            + "the groups with the capital letter mu (for myriad).  Capital letters are used "
+            + "for values below 10,000; small letters for higher numbers (to make the capital "
+            + "mu stand out).",
+
+            "This is a custom (user-defined) rule set."
+        };
+}
diff --git a/src/com/ibm/demo/translit/Demo.java b/src/com/ibm/demo/translit/Demo.java
new file mode 100755
index 0000000..83b91fb
--- /dev/null
+++ b/src/com/ibm/demo/translit/Demo.java
@@ -0,0 +1,266 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/translit/Attic/Demo.java,v $ 
+ * $Date: 2000/03/10 03:47:44 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.translit;
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import com.ibm.text.components.*;
+import com.ibm.text.*;
+
+/**
+ * A frame that allows the user to experiment with keyboard
+ * transliteration.  This class has a main() method so it can be run
+ * as an application.  The frame contains an editable text component
+ * and uses keyboard transliteration to process keyboard events.
+ *
+ * <p>Copyright (c) IBM Corporation 1999.  All rights reserved.
+ *
+ * @author Alan Liu
+ * @version $RCSfile: Demo.java,v $ $Revision: 1.4 $ $Date: 2000/03/10 03:47:44 $
+ */
+public class Demo extends Frame {
+
+    static final boolean DEBUG = false;
+
+    Transliterator translit = null;
+
+    boolean compound = false;
+    Transliterator[] compoundTranslit = new Transliterator[MAX_COMPOUND];
+    static final int MAX_COMPOUND = 128;
+    int compoundCount = 0;
+
+    TransliteratingTextComponent text = null;
+
+    Menu translitMenu;
+    CheckboxMenuItem translitItem;
+    CheckboxMenuItem noTranslitItem;
+
+    static final String NO_TRANSLITERATOR = "None";
+
+    private static final String COPYRIGHT =
+        "\u00A9 IBM Corporation 1999. All rights reserved.";
+
+    public static void main(String[] args) {
+        Frame f = new Demo(600, 200);
+        f.addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent e) {
+                System.exit(0);
+            }
+        });
+        f.setVisible(true);
+    }
+
+	public Demo(int width, int height) {
+        super("Transliteration Demo");
+
+        initMenus();
+
+        addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent e) {
+                handleClose();
+            }
+        });
+        
+        text = new TransliteratingTextComponent();
+        Font font = new Font("serif", Font.PLAIN, 48);
+        text.setFont(font);
+        text.setSize(width, height);
+        text.setVisible(true);
+        text.setText("\u03B1\u05D0\u3042\u4E80");
+        add(text);
+
+        setSize(width, height);
+    }
+
+    private void initMenus() {
+        MenuBar mbar;
+        Menu menu;
+        MenuItem mitem;
+        CheckboxMenuItem citem;
+        
+        setMenuBar(mbar = new MenuBar());
+        mbar.add(menu = new Menu("File"));
+        menu.add(mitem = new MenuItem("Quit"));
+        mitem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                handleClose();
+            }
+        });
+
+        final ItemListener setTransliteratorListener = new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                CheckboxMenuItem item = (CheckboxMenuItem) e.getSource();
+                if (e.getStateChange() == ItemEvent.DESELECTED) {
+                    // Don't let the current transliterator be deselected.
+                    // Just reselect it.
+                    item.setState(true);
+                } else if (compound) {
+                    // Adding an item to a compound transliterator
+                    handleAddToCompound(item.getLabel());
+                } else if (item != translitItem) {
+                    // Deselect previous choice.  Don't need to call
+                    // setState(true) on new choice.
+                    translitItem.setState(false);
+                    translitItem = item;
+                    handleSetTransliterator(item.getLabel());
+                }
+            }
+        };
+
+        translit = null;
+        mbar.add(translitMenu = new Menu("Transliterator"));
+        translitMenu.add(translitItem = noTranslitItem =
+                         new CheckboxMenuItem(NO_TRANSLITERATOR, true));
+        noTranslitItem.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                // Can't uncheck None -- any action here sets None to true
+                setNoTransliterator();
+            }
+        });
+
+        translitMenu.addSeparator();
+
+        translitMenu.add(citem = new CheckboxMenuItem("Compound"));
+        citem.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                CheckboxMenuItem item = (CheckboxMenuItem) e.getSource();
+                if (e.getStateChange() == ItemEvent.DESELECTED) {
+                    // If compound gets deselected, then select NONE
+                    setNoTransliterator();
+                } else if (!compound) {
+                    // Switching from non-compound to compound
+                    translitItem.setState(false);
+                    translitItem = item;
+                    translit = null;
+                    compound = true;
+                    compoundCount = 0;
+                    for (int i=0; i<MAX_COMPOUND; ++i) {
+                        compoundTranslit[i] = null;
+                    }
+                }
+            }
+        });
+      
+        translitMenu.addSeparator();
+
+        for (Enumeration e=getSystemTransliteratorNames().elements();
+             e.hasMoreElements(); ) {
+            String s = (String) e.nextElement();
+            translitMenu.add(citem = new CheckboxMenuItem(s));
+            citem.addItemListener(setTransliteratorListener);
+        }
+
+        mbar.add(menu = new Menu("Batch"));
+        menu.add(mitem = new MenuItem("Transliterate Selection"));
+        mitem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                handleBatchTransliterate();
+            }
+        });
+    }
+
+    /**
+     * Get a sorted list of the system transliterators.
+     */
+    private static Vector getSystemTransliteratorNames() {
+        Vector v = new Vector();
+        for (Enumeration e=Transliterator.getAvailableIDs();
+             e.hasMoreElements(); ) {
+            v.addElement(e.nextElement());
+        }
+        // Insertion sort, O(n^2) acceptable for small n
+        for (int i=0; i<(v.size()-1); ++i) {
+            String a = (String) v.elementAt(i);
+            for (int j=i+1; j<v.size(); ++j) {
+                String b = (String) v.elementAt(j);
+                if (a.compareTo(b) > 0) {
+                    v.setElementAt(b, i);
+                    v.setElementAt(a, j);
+                    a = b;
+                }
+            }
+        }
+        return v;
+    }
+
+    private void setNoTransliterator() {
+        translitItem = noTranslitItem;
+        noTranslitItem.setState(true);
+        handleSetTransliterator(noTranslitItem.getLabel());
+        compound = false;
+        for (int i=0; i<translitMenu.getItemCount(); ++i) {
+            MenuItem it = translitMenu.getItem(i);
+            if (it != noTranslitItem && it instanceof CheckboxMenuItem) {
+                ((CheckboxMenuItem) it).setState(false);
+            }
+        }
+    }
+
+    private void handleAddToCompound(String name) {
+        if (compoundCount < MAX_COMPOUND) {
+            compoundTranslit[compoundCount] = decodeTranslitItem(name);
+            ++compoundCount;
+            Transliterator t[] = new Transliterator[compoundCount];
+            System.arraycopy(compoundTranslit, 0, t, 0, compoundCount);
+            translit = new CompoundTransliterator(t);
+            text.setTransliterator(translit);
+        }
+    }
+
+    private void handleSetTransliterator(String name) {
+        translit = decodeTranslitItem(name);
+        text.setTransliterator(translit);
+    }
+
+    /**
+     * Decode a menu item that looks like <translit name>.
+     */
+    private static Transliterator decodeTranslitItem(String name) {
+        return (name.equals(NO_TRANSLITERATOR))
+            ? null : Transliterator.getInstance(name);
+    }
+
+    private void handleBatchTransliterate() {
+        if (translit == null) {
+            return;
+        }
+
+        int start = text.getSelectionStart();
+        int end = text.getSelectionEnd();
+        ReplaceableString s =
+            new ReplaceableString(text.getText().substring(start, end));
+
+        StringBuffer log = null;
+        if (DEBUG) {
+            log = new StringBuffer();
+            log.append('"' + s.toString() + "\" (start " + start +
+                       ", end " + end + ") -> \"");
+        }
+
+        translit.transliterate(s);
+        String str = s.toString();
+
+        if (DEBUG) {
+            log.append(str + "\"");
+            System.out.println("Batch " + translit.getID() + ": " + log.toString());
+        }
+
+        text.replaceRange(str, start, end);
+        text.select(start, start + str.length());
+    }
+
+    private void handleClose() {
+        dispose();
+    }
+}
diff --git a/src/com/ibm/demo/translit/DemoApplet.java b/src/com/ibm/demo/translit/DemoApplet.java
new file mode 100755
index 0000000..77fb815
--- /dev/null
+++ b/src/com/ibm/demo/translit/DemoApplet.java
@@ -0,0 +1,74 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/translit/Attic/DemoApplet.java,v $ 
+ * $Date: 2000/03/10 03:47:44 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.demo.translit;
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.*;
+import com.ibm.text.components.AppletFrame;
+
+/**
+ * A simple Applet that shows a button.  When pressed, the button
+ * shows the DemoAppletFrame.  This Applet is meant to be embedded
+ * in a web page.
+ *
+ * <p>Copyright (c) IBM Corporation 1999.  All rights reserved.
+ *
+ * @author Alan Liu
+ * @version $RCSfile: DemoApplet.java,v $ $Revision: 1.4 $ $Date: 2000/03/10 03:47:44 $
+ */
+public class DemoApplet extends Applet {
+
+    Demo frame = null;
+    
+    private static final String COPYRIGHT =
+        "\u00A9 IBM Corporation 1999. All rights reserved.";
+
+    public static void main(String args[]) {
+        final DemoApplet applet = new DemoApplet();
+        new AppletFrame("Transliteration Demo", applet, 640, 480);
+    }
+
+	public void init() {
+
+		Button button = new Button("Transliteration Demo");
+		button.addActionListener(new ActionListener() {
+		    public void actionPerformed(ActionEvent e) {
+		        if (frame == null) {
+                    frame = new Demo(600, 200);
+                    frame.addWindowListener(new WindowAdapter() {
+                        public void windowClosing(WindowEvent we) {
+                            frame = null;
+                        }
+                    });
+                }
+                frame.setVisible(true);
+                frame.toFront();
+		    }
+		});
+
+		add(button);
+
+        Dimension size = button.getPreferredSize();
+        size.width += 10;
+        size.height += 10;
+
+		resize(size);
+	}
+	
+    public void stop() {
+        if (frame != null) {
+            frame.dispose();
+        }
+        frame = null;
+    }
+}
diff --git a/src/com/ibm/demo/translit/demo.bat b/src/com/ibm/demo/translit/demo.bat
new file mode 100755
index 0000000..36f8b78
--- /dev/null
+++ b/src/com/ibm/demo/translit/demo.bat
@@ -0,0 +1,19 @@
+REM /*
+REM *******************************************************************************
+REM  * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+REM  * others. All Rights Reserved.                                                *
+REM  *******************************************************************************
+REM  *
+REM  * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/translit/Attic/demo.bat,v $ 
+REM  * $Date: 2000/03/10 03:47:44 $ 
+REM  * $Revision: 1.2 $
+REM  *
+REM  *****************************************************************************************
+REM  */
+REM For best results, run the demo as an applet inside of Netscape
+REM with Bitstream Cyberbit installed.
+
+REM setup your JDK 1.1.x path and classpath here:
+call JDK11
+set CLASSPATH=../translit.jar;%CLASSPATH%
+javaw Demo
diff --git a/src/com/ibm/demo/translit/demo.html b/src/com/ibm/demo/translit/demo.html
new file mode 100755
index 0000000..a5e7ba3
--- /dev/null
+++ b/src/com/ibm/demo/translit/demo.html
@@ -0,0 +1,27 @@
+<HTML>
+<HEAD>
+<TITLE>Transliteration Demo</TITLE>
+</HEAD>
+<BODY>
+
+<APPLET CODE="com.ibm.demo.translit.DemoApplet.class" WIDTH=140 HEIGHT=33></APPLET>
+
+<HR>
+
+If you don't see a button above, then your browser is failing to
+locate the necessary Java class files.
+
+<P>
+
+One way to make this work is to copy this HTML file to
+<code>jcu/src</code>, and make sure the Java files in the directories
+under <code>jcu/src/com</code> are built.  Then open this HTML file
+using a browser or appletviewer.
+
+<P>
+
+For best results, run this demo as an applet within Netscape with
+Bitstream Cyberbit installed.
+
+</BODY>
+</HTML>
diff --git a/src/com/ibm/test/TestFmwk.java b/src/com/ibm/test/TestFmwk.java
new file mode 100755
index 0000000..0c4104e
--- /dev/null
+++ b/src/com/ibm/test/TestFmwk.java
@@ -0,0 +1,262 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/Attic/TestFmwk.java,v $ 
+ * $Date: 2000/03/10 03:47:44 $ 
+ * $Revision: 1.8 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test;
+
+import java.lang.reflect.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.io.*;
+import java.text.*;
+
+
+
+/**
+ * BITestFmwk is a base class for tests that can be run conveniently from
+ * the command line as well as under the Java test harness.
+ * <p>
+ * Sub-classes implement a set of methods named Test<something>. Each
+ * of these methods performs some test. Test methods should indicate
+ * errors by calling either err or errln.  This will increment the
+ * errorCount field and may optionally print a message to the log.
+ * Debugging information may also be added to the log via the log
+ * and logln methods.  These methods will add their arguments to the
+ * log only if the test is being run in verbose mode.
+ */
+
+public class TestFmwk {
+
+    /**
+     * Puts a copyright in the .class file
+     */
+    private static final String copyrightNotice
+        = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
+
+    //------------------------------------------------------------------------
+    // Everything below here is boilerplate code that makes it possible
+    // to add a new test by simply adding a function to an existing class
+    //------------------------------------------------------------------------
+
+	protected TestFmwk() {
+        // Create a hashtable containing all the test methods.
+        testMethods = new Hashtable();
+        Method[] methods = getClass().getDeclaredMethods();
+        for( int i=0; i<methods.length; i++ ) {
+            if( methods[i].getName().startsWith("Test") 
+					|| methods[i].getName().startsWith("test")) {
+                testMethods.put( methods[i].getName(), methods[i] );
+            }
+        }
+    }
+    
+    public void run(String[] args) throws Exception {
+    	if (params == null) params = new TestParams();
+        // Parse the test arguments.  They can be either the flag
+        // "-verbose" or names of test methods. Create a list of
+        // tests to be run.
+        testsToRun = new Vector(args.length);
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].equals("-verbose")) {
+                params.verbose = true;
+            }
+            else if (args[i].equals("-prompt")) {
+                params.prompt = true;
+            } else if (args[i].equals("-nothrow")) {
+                params.nothrow = true;
+            } else {
+                Object m = testMethods.get(args[i]);
+                if (m != null) {
+                    testsToRun.addElement(m);
+                } else {
+                    usage();
+                    return;
+                }
+            }
+        }
+
+    	if (params == null) params = new TestParams();
+        System.out.println(getClass().getName() + " {");
+        params.indentLevel++;
+		Enumeration methodsToRun;
+		
+		if (testsToRun.size() < 1) {
+			methodsToRun = testMethods.elements();
+		} else {
+			methodsToRun = testsToRun.elements();
+		}
+
+        // Run the list of tests given in the test arguments
+        while (methodsToRun.hasMoreElements()) {
+            int oldCount = params.errorCount;
+
+           	Method testMethod = (Method)methodsToRun.nextElement();
+            writeTestName(testMethod.getName());
+
+            try {
+                testMethod.invoke(this, new Object[0]);
+            }
+            catch( IllegalAccessException e ) {
+                errln("Can't acces test method " + testMethod.getName());
+            } catch( InvocationTargetException e ) {
+                errln("Uncaught exception \""+e+"\"thrown in test method "
+                        + testMethod.getName());
+                e.getTargetException().printStackTrace(this.params.log);
+            }
+            writeTestResult(params.errorCount - oldCount);
+        }
+        params.indentLevel--;
+        writeTestResult(params.errorCount);
+
+        if (params.prompt) {
+            System.out.println("Hit RETURN to exit...");
+            try {
+                System.in.read();
+            } catch (IOException e) {
+                System.out.println("Exception: " + e.toString() + e.getMessage());
+            }
+        }
+        if (params.nothrow) {
+            System.exit(params.errorCount);
+        }
+    }
+
+	protected void run(TestFmwk childTest) throws Exception {
+		childTest.setParent(this);
+		childTest.run(new String[0]);
+	}
+
+    private void setParent(TestFmwk parent) {
+    	params = parent.params;
+    }
+
+    /**
+     * Adds given string to the log if we are in verbose mode.
+     */
+    protected void log( String message ) {
+        if( params.verbose ) {
+            indent(params.indentLevel + 1);
+            params.log.print( message );
+            params.log.flush();
+        }
+    }
+
+    protected void logln( String message ) {
+        log(message + System.getProperty("line.separator"));
+    }
+
+    /**
+     * Report an error
+     */
+    protected void err( String message ) {
+        params.errorCount++;
+        indent(params.indentLevel + 1);
+        params.log.print( message );
+        params.log.flush();
+
+        if (!params.nothrow) {
+            throw new RuntimeException(message);
+        }
+    }
+
+    protected void errln( String message ) {
+        err(message + System.getProperty("line.separator"));
+    }
+
+    protected int getErrorCount() {
+        return params.errorCount;
+    }
+
+    protected void writeTestName(String testName) {
+        indent(params.indentLevel);
+        params.log.print(testName);
+        params.log.flush();
+        params.needLineFeed = true;
+    }
+
+    protected void writeTestResult(int count) {
+        if (!params.needLineFeed) {
+            indent(params.indentLevel);
+            params.log.print("}");
+        }
+        params.needLineFeed = false;
+
+        if (count != 0) {
+            params.log.println(" FAILED");
+        } else {
+            params.log.println(" Passed");
+        }
+    }
+
+    private final void indent(int distance) {
+        if (params.needLineFeed) {
+            params.log.println(" {");
+            params.needLineFeed = false;
+        }
+        params.log.print(spaces.substring(0, distance * 2));
+    }
+
+    /**
+     * Print a usage message for this test class.
+     */
+    void usage() {
+        System.out.println(getClass().getName() +
+                ": [-verbose] [-nothrow] [-prompt] [test names]");
+
+        System.out.println("test names:");
+        Enumeration methodNames = testMethods.keys();
+        while( methodNames.hasMoreElements() ) {
+            System.out.println("\t" + methodNames.nextElement() );
+        }
+    }
+
+    public static String hex(char ch) {
+        StringBuffer result = new StringBuffer();
+        String foo = Integer.toString(ch,16).toUpperCase();
+        for (int i = foo.length(); i < 4; ++i) {
+            result.append('0');
+        }
+        return result + foo;
+    }
+
+    public static String hex(String s) {
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < s.length(); ++i) {
+            if (i != 0) result.append(',');
+            result.append(hex(s.charAt(i)));
+        }
+        return result.toString();
+    }
+
+    public static String hex(StringBuffer s) {
+        return hex(s.toString());
+    }
+
+	private static class TestParams {
+    	public boolean   prompt = false;
+    	public boolean   nothrow = false;
+    	public boolean   verbose = false;
+
+    	public PrintWriter log = new PrintWriter(System.out,true);
+    	public int         indentLevel = 0;
+    	public boolean     needLineFeed = false;
+    	public int         errorCount = 0;
+    }
+
+	private TestParams params = null;
+    private Hashtable testMethods;
+	private Vector testsToRun;
+    private final String spaces = "                                          ";
+}
+
+
+
diff --git a/src/com/ibm/test/bnf/BigNumberFormatTest.java b/src/com/ibm/test/bnf/BigNumberFormatTest.java
new file mode 100755
index 0000000..b361941
--- /dev/null
+++ b/src/com/ibm/test/bnf/BigNumberFormatTest.java
@@ -0,0 +1,355 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/bnf/Attic/BigNumberFormatTest.java,v $ 
+ * $Date: 2000/03/10 03:47:44 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.bnf;
+
+import com.ibm.test.*;
+import com.ibm.text.*;
+import java.text.ParseException;
+import java.util.*;
+import java.math.BigInteger;
+
+/**
+ * @test
+ * General test of Big NumberFormat
+ */
+public class BigNumberFormatTest extends TestFmwk {
+
+    static final int ILLEGAL = -1;
+
+    public static void main(String[] args) throws Exception {
+        new BigNumberFormatTest().run(args);
+    }
+    	
+    public void TestExponent() {
+        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
+        DecimalFormat fmt1 = new DecimalFormat("0.###E0", US);
+        DecimalFormat fmt2 = new DecimalFormat("0.###E+0", US);
+        Number n = new Long(1234);
+        expect(fmt1, n, "1.234E3");
+        expect(fmt2, n, "1.234E+3");
+        expect(fmt1, "1.234E3", n);
+        expect(fmt1, "1.234E+3", n); // Either format should parse "E+3"
+        expect(fmt2, "1.234E+3", n);
+    }
+
+    private void expectPad(DecimalFormat fmt, String pat, int pos) {
+        expectPad(fmt, pat, pos, 0, (char)0);
+    }
+
+    private void expectPad(DecimalFormat fmt, String pat,
+                           int pos, int width, char pad) {
+        int apos = 0, awidth = 0;
+        char apad = 0;
+        try {
+            fmt.applyPattern(pat);
+            apos = fmt.getPadPosition();
+            awidth = fmt.getFormatWidth();
+            apad = fmt.getPadCharacter();
+        } catch (IllegalArgumentException e) {
+            apos = -1;
+            awidth = width;
+            apad = pad;
+        }
+        if (apos == pos && awidth == width && apad == pad) {
+            logln("Ok   \"" + pat + "\" pos=" + apos +
+                  ((pos == -1) ? "" : " width=" + awidth + " pad=" + apad));
+        } else {
+            logln("FAIL \"" + pat + "\" pos=" + apos +
+                  " width=" + awidth + " pad=" + apad +
+                  ", expected " + pos + " " + width + " " + pad);
+        }
+    }
+
+    /**
+     */
+    public void TestPatterns() {
+        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
+        DecimalFormat fmt = new DecimalFormat("#", US);
+
+        expectPad(fmt, "*^#", DecimalFormat.PAD_BEFORE_PREFIX, 1, '^');
+        expectPad(fmt, "$*^#", DecimalFormat.PAD_AFTER_PREFIX, 2, '^');
+        expectPad(fmt, "#*^", DecimalFormat.PAD_BEFORE_SUFFIX, 1, '^');
+        expectPad(fmt, "#$*^", DecimalFormat.PAD_AFTER_SUFFIX, 2, '^');
+        expectPad(fmt, "$*^$#", ILLEGAL);
+        expectPad(fmt, "#$*^$", ILLEGAL);
+        expectPad(fmt, "'pre'#,##0*x'post'", DecimalFormat.PAD_BEFORE_SUFFIX,
+                  12, 'x');
+        expectPad(fmt, "''#0*x", DecimalFormat.PAD_BEFORE_SUFFIX,
+                  3, 'x');
+        expectPad(fmt, "'I''ll'*a###.##", DecimalFormat.PAD_AFTER_PREFIX,
+                  10, 'a');
+
+        fmt.applyPattern("AA#,##0.00ZZ");
+        fmt.setPadCharacter('^');
+
+        fmt.setFormatWidth(10);
+
+        fmt.setPadPosition(DecimalFormat.PAD_BEFORE_PREFIX);
+        expectPat(fmt, "*^AA#,##0.00ZZ");
+
+        fmt.setPadPosition(DecimalFormat.PAD_BEFORE_SUFFIX);
+        expectPat(fmt, "AA#,##0.00*^ZZ");
+
+        fmt.setPadPosition(DecimalFormat.PAD_AFTER_SUFFIX);
+        expectPat(fmt, "AA#,##0.00ZZ*^");
+
+        //            12  3456789012
+        String exp = "AA*^#,##0.00ZZ";
+        fmt.setFormatWidth(12);
+        fmt.setPadPosition(DecimalFormat.PAD_AFTER_PREFIX);
+        expectPat(fmt, exp);
+
+        fmt.setFormatWidth(13);
+        //              12  34567890123
+        expectPat(fmt, "AA*^##,##0.00ZZ");
+
+        fmt.setFormatWidth(14);
+        //              12  345678901234
+        expectPat(fmt, "AA*^###,##0.00ZZ");
+
+        fmt.setFormatWidth(15);
+        //              12  3456789012345
+        expectPat(fmt, "AA*^####,##0.00ZZ"); // This is the interesting case
+
+        fmt.setFormatWidth(16);
+        //              12  34567890123456
+        expectPat(fmt, "AA*^#,###,##0.00ZZ");
+    }
+
+    private void expectPat(DecimalFormat fmt, String exp) {
+        String pat = fmt.toPattern();
+        if (pat.equals(exp)) {
+            logln("Ok   \"" + pat + '"');
+        } else {
+            errln("FAIL \"" + pat + "\", expected \"" + exp + '"');
+        }
+    }
+
+//This has been removed pending addition of com.ibm.math package to ICU4J
+    /*
+     * Test the handling of the AlphaWorks BigDecimal
+     *
+    public void TestAlphaBigDecimal() {
+        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
+        expect(NumberFormat.getScientificInstance(Locale.US),
+               new Number[] { new com.ibm.math.BigDecimal("12345.678901"),
+                           },
+               "1.234568E4");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Number[] { new com.ibm.math.BigDecimal("12345.4999"),
+                              new com.ibm.math.BigDecimal("12344.5001"),
+                            },
+               "12.345E3");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Number[] { new com.ibm.math.BigDecimal("12345.5000"),
+                              new com.ibm.math.BigDecimal("12346.5000"),
+                            },
+               "12.346E3");
+    } */
+
+    /**
+     */
+    public void TestScientific() {
+        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
+        expect(NumberFormat.getScientificInstance(Locale.US),
+               new Number[] { new Double(12345.678901),
+                              new java.math.BigDecimal("12345.678901"),
+                            },
+               "1.234568E4");
+        expect(new DecimalFormat("##0.###E0", US),
+               new Double(12345),
+               "12.34E3");
+        expect(new DecimalFormat("##0.###E0", US),
+               new Double(12345.00001),
+               "12.35E3");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Number[] { new Integer(12345),
+                              new Long(12345),
+                              new java.math.BigDecimal("12345.4999"),
+                              new java.math.BigDecimal("12344.5001"),
+                            },
+               "12.345E3");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Number[] { new java.math.BigDecimal("12345.5000"),
+                              new java.math.BigDecimal("12346.5000"),
+                            },
+               "12.346E3");
+        expect(NumberFormat.getScientificInstance(Locale.FRANCE),
+               new Double(12345.678901),
+               "1,234568E4");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Double(789.12345e-9),
+               "789.12E-9");
+        expect(new DecimalFormat("##0.####E0", US),
+               new Double(780.e-9),
+               "780E-9");
+        expect(new DecimalFormat(".###E0", US),
+               new Double(45678),
+               ".457E5");
+        expect(new DecimalFormat(".###E0", US),
+               new Long(0),
+               ".0E0");
+        expect(new DecimalFormat[] { new DecimalFormat("#E0", US),
+                                     new DecimalFormat("##E0", US),
+                                     new DecimalFormat("####E0", US),
+                                     new DecimalFormat("0E0", US),    
+                                     new DecimalFormat("00E0", US),   
+                                     new DecimalFormat("000E0", US), 
+                                   },
+               new Long(45678000),
+               new String[] { "4.5678E7",
+                              "45.678E6",
+                              "4567.8E4",
+                              "5E7",
+                              "46E6",  
+                              "457E5",
+                            }
+               );
+        expect(new DecimalFormat("###E0", US),
+               new Object[] { new Double(0.0000123), "12.3E-6",
+                              new Double(0.000123), "123E-6",
+                              new java.math.BigDecimal("0.00123"), "1.23E-3", // Cafe VM messes up Double(0.00123)
+                              new Double(0.0123), "12.3E-3",
+                              new Double(0.123), "123E-3",
+                              new Double(1.23), "1.23E0",
+                              new Double(12.3), "12.3E0",
+                              new Double(123), "123E0",
+                              new Double(1230), "1.23E3",
+                             });
+        expect(new DecimalFormat("0.#E+00", US),
+               new Object[] { new Double(0.00012), "1.2E-04",
+                              new Long(12000),     "1.2E+04",
+                             });
+    }
+
+    /**
+     */
+    public void TestPad() {
+        DecimalFormatSymbols US = new DecimalFormatSymbols(Locale.US);
+        expect(new DecimalFormat("*^##.##", US),
+               new Object[] { new Long(0),      "^^^^0",
+                              new Double(-1.3), "^-1.3",
+                            }
+               );
+        expect(new DecimalFormat("##0.0####E0*_ g-m/s^2", US),
+               new Object[] { new Long(0),       "0.0E0______ g-m/s^2",
+                              new Double(1.0/3), "333.333E-3_ g-m/s^2",
+                            }
+               );
+        expect(new DecimalFormat("##0.0####*_ g-m/s^2", US),
+               new Object[] { new Long(0),       "0.0______ g-m/s^2",
+                              new Double(1.0/3), "0.33333__ g-m/s^2",
+                            }
+               );
+    }
+    
+    private void expect(NumberFormat fmt, Object[] data) {
+        for (int i=0; i<data.length; i+=2) {
+            expect(fmt, (Number) data[i], (String) data[i+1]);
+        }
+    }
+    
+    private void expect(Object fmto, Object numo, Object expo) {
+        NumberFormat fmt = null, fmts[] = null;
+        Number num = null, nums[] = null;
+        String exp = null, exps[] = null;
+        if (fmto instanceof NumberFormat[]) {
+            fmts = (NumberFormat[]) fmto;
+        } else {
+            fmt = (NumberFormat) fmto;
+        }
+        if (numo instanceof Number[]) {
+            nums = (Number[]) numo;
+        } else {
+            num = (Number) numo;
+        }
+        if (expo instanceof String[]) {
+            exps = (String[]) expo;
+        } else {
+            exp = (String) expo;
+        }
+        int n = 1;
+        if (fmts != null) {
+            n = Math.max(n, fmts.length);
+        }
+        if (nums != null) {
+            n = Math.max(n, nums.length);
+        }
+        if (exps != null) {
+            n = Math.max(n, exps.length);
+        }
+        for (int i=0; i<n; ++i) {
+            expect(fmts == null ? fmt : fmts[i],
+                   nums == null ? num : nums[i],
+                   exps == null ? exp : exps[i]);
+        }
+    }
+
+    private static String showNumber(Number n) {
+        String cls = n.getClass().getName();
+        //This has been removed pending addition of com.ibm.math package to ICU4J
+        if (!(/*n instanceof com.ibm.math.BigDecimal
+              ||*/ n instanceof java.math.BigDecimal)) {
+            int i = cls.lastIndexOf('.');
+            cls = cls.substring(i+1);
+        }
+        return n.toString() + " (" + cls + ')';
+    }
+
+    private void expect(NumberFormat fmt, Number n, String exp) {
+        String saw = fmt.format(n);
+        String pat = ((DecimalFormat) fmt).toPattern();
+        if (saw.equals(exp)) {
+            logln("Ok   " + showNumber(n) + " x " +
+                  pat + " = \"" +
+                  saw + '"');
+        } else {
+            errln("FAIL " + showNumber(n) + " x " +
+                  pat + " = \"" +
+                  saw + "\", expected \"" + exp + '"');
+        }
+    }
+
+    private void expect(NumberFormat fmt, String str, Number exp) {
+        Number saw = null;
+        try {
+            saw = fmt.parse(str);
+        } catch (ParseException e) {
+            saw = null;
+        }
+        String pat = ((DecimalFormat) fmt).toPattern();
+        if (saw.equals(exp)) {
+            logln("Ok   \"" + str + "\" x " +
+                  pat + " = " +
+                  showNumber(saw));
+        } else {
+            errln("FAIL \"" + str + "\" x " +
+                  pat + " = " +
+                  showNumber(saw) + ", expected " + showNumber(exp));
+        }
+    }
+
+    public void Test4161100() {
+        NumberFormat f = NumberFormat.getInstance();
+        f.setMinimumFractionDigits(1);
+        f.setMaximumFractionDigits(1);
+        double a = -0.09;
+        String s = f.format(a);
+        logln(a + " x " +
+              ((DecimalFormat) f).toPattern() + " = " +
+              s);
+        if (!s.equals("-0.1")) {
+            errln("FAIL");
+        }
+    }        
+}
diff --git a/src/com/ibm/test/calendar/AstroTest.java b/src/com/ibm/test/calendar/AstroTest.java
new file mode 100755
index 0000000..5db2e82
--- /dev/null
+++ b/src/com/ibm/test/calendar/AstroTest.java
@@ -0,0 +1,74 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/calendar/Attic/AstroTest.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.calendar;
+
+// AstroTest
+
+import com.ibm.test.*;
+import java.util.*;
+import com.ibm.util.*;
+
+import com.ibm.util.CalendarAstronomer.*;
+
+// TODO: try finding next new moon after  07/28/1984 16:00 GMT
+
+public class AstroTest extends TestFmwk {
+    public static void main(String[] args) throws Exception {
+        new AstroTest().run(args);
+    }
+
+    static final double PI = Math.PI;
+    
+    static GregorianCalendar gc = new GregorianCalendar(new SimpleTimeZone(0, "UTC"));
+    static CalendarAstronomer astro = new CalendarAstronomer();
+    
+    public void TestSolarLongitude() {
+        final double tests[][] = {
+            { 1980, 7, 27, 00, 00, 124.114347 },
+            { 1988, 7, 27, 00, 00, 124.187732 },
+        };
+        logln("");
+        for (int i = 0; i < tests.length; i++) {
+            gc.clear();
+            gc.set((int)tests[i][0], (int)tests[i][1]-1, (int)tests[i][2], (int)tests[i][3], (int) tests[i][4]);
+            
+            astro.setDate(gc.getTime());
+            
+            double longitude = astro.getSunLongitude();
+            
+            Equatorial result = astro.getSunPosition();
+        }
+    }
+    
+    public void TestLunarPosition() {
+        final double tests[][] = {
+            { 1979, 2, 26, 16, 00,  0, 0 },
+        };
+        logln("");
+        
+        for (int i = 0; i < tests.length; i++) {
+            gc.clear();
+            gc.set((int)tests[i][0], (int)tests[i][1]-1, (int)tests[i][2], (int)tests[i][3], (int) tests[i][4]);
+            astro.setDate(gc.getTime());
+            
+            Equatorial result = astro.getMoonPosition();
+        }
+
+    }
+    
+    public void TestCoordinates() {
+        Equatorial result = astro.eclipticToEquatorial(139.686111 * PI/ 180.0, 4.875278* PI / 180.0);
+        logln("result is " + result + ";  " + result.toHmsString());
+    }
+    
+}
\ No newline at end of file
diff --git a/src/com/ibm/test/calendar/CalendarTest.java b/src/com/ibm/test/calendar/CalendarTest.java
new file mode 100755
index 0000000..5b43c05
--- /dev/null
+++ b/src/com/ibm/test/calendar/CalendarTest.java
@@ -0,0 +1,171 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/calendar/Attic/CalendarTest.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.test.calendar;
+
+import com.ibm.test.*;
+import java.text.*;
+import java.util.*;
+import com.ibm.util.*;
+
+/**
+ * A base class for classes that test individual Calendar subclasses.
+ * Defines various useful utility methods and constants
+ */
+public class CalendarTest extends TestFmwk {
+    
+    // Constants for use by subclasses, solely to save typing
+    public final static int SUN = Calendar.SUNDAY;
+    public final static int MON = Calendar.MONDAY;
+    public final static int TUE = Calendar.TUESDAY;
+    public final static int WED = Calendar.WEDNESDAY;
+    public final static int THU = Calendar.THURSDAY;
+    public final static int FRI = Calendar.FRIDAY;
+    public final static int SAT = Calendar.SATURDAY;
+
+    public final static int ERA     = Calendar.ERA;
+    public final static int YEAR    = Calendar.YEAR;
+    public final static int MONTH   = Calendar.MONTH;
+    public final static int DATE    = Calendar.DATE;
+    public final static int HOUR    = Calendar.HOUR;
+    public final static int MINUTE  = Calendar.MINUTE;
+    public final static int SECOND  = Calendar.SECOND;
+    public final static int DOY     = Calendar.DAY_OF_YEAR;
+    public final static int WOY     = Calendar.WEEK_OF_YEAR;
+    public final static int WOM     = Calendar.WEEK_OF_MONTH;
+    public final static int DOW     = Calendar.DAY_OF_WEEK;
+    public final static int DOWM    = Calendar.DAY_OF_WEEK_IN_MONTH;
+    
+    public final static SimpleTimeZone UTC = new SimpleTimeZone(0, "GMT");
+
+    final String pattern = "E, MM/dd/yyyy G HH:mm:ss.S z";
+    
+    /**
+     * Iterates through a list of calendar <code>TestCase</code> objects and
+     * makes sure that the time-to-fields and fields-to-time calculations work
+     * correnctly for the values in each test case.
+     */
+    public void doTestCases(TestCase[] cases, Calendar cal)
+    {
+        cal.setTimeZone(UTC);
+        
+        // Get a format to use for printing dates in the calendar system we're testing
+        // TODO: This is kind of ugly right now . 
+        DateFormat format = IBMCalendar.getDateTimeFormat(cal, DateFormat.SHORT, -1, Locale.getDefault());
+        
+        ((SimpleDateFormat)format).applyPattern(pattern);
+        DateFormat testFmt = (DateFormat)format.clone();
+
+        // This format is used for pringing Gregorian dates.  This one is easier
+        DateFormat gregFormat = new SimpleDateFormat(pattern);
+        gregFormat.setTimeZone(UTC);
+        
+        // Now iterate through the test cases and see what happens
+        for (int i = 0; i < cases.length; i++)
+        {
+            TestCase test = cases[i];
+            testFmt.setCalendar(test);
+            
+            //
+            // First we want to make sure that the millis -> fields calculation works
+            // test.applyTime will call setTime() on the calendar object, and
+            // test.fieldsEqual will retrieve all of the field values and make sure
+            // that they're the same as the ones in the testcase
+            //
+            test.applyTime(cal);
+            if (!test.fieldsEqual(cal)) {
+                errln("ERROR: millis --> fields calculation incorrect for "
+                        + gregFormat.format(test.getTime()));
+                logln("  expected " + testFmt.format(test.getTime()));
+                logln("  got      " + format.format(cal.getTime()) );
+            }
+            else {
+                //
+                // If that was OK, check the fields -> millis calculation
+                // test.applyFields will set all of the calendar's fields to 
+                // match those in the test case.
+                //
+                cal.setTime(new Date(0));
+                test.applyFields(cal);
+                
+                if (!test.equals(cal)) {
+                    errln("ERROR: fields --> millis calculation incorrect for "
+                        + testFmt.format(test.getTime()));
+                    logln("  expected " + test.getTime().getTime());
+                    logln("  got      " + cal.getTime().getTime() );
+                }
+            }
+        }
+    }
+    
+    static public final boolean ROLL = true;
+    static public final boolean ADD = false;
+    
+    /**
+     * Process test cases for <code>add</code> and <code>roll</code> methods.
+     * Each test case is an array of integers, as follows:
+     * <ul>
+     *  <li>0: input year
+     *  <li>1:       month  (zero-based)
+     *  <li>2:       day
+     *  <li>3: field to roll or add to
+     *  <li>4: amount to roll or add
+     *  <li>5: result year
+     *  <li>6:        month (zero-based)
+     *  <li>7:        day
+     * </ul>
+     * For example:
+     * <pre>
+     *   //       input                add by          output
+     *   //  year  month     day     field amount    year  month     day
+     *   {   5759, HESHVAN,   2,     MONTH,   1,     5759, KISLEV,    2 },
+     * </pre>
+     *
+     * @param roll  <code>true</code> or <code>ROLL</code> to test the <code>roll</code> method;
+     *              <code>false</code> or <code>ADD</code> to test the <code>add</code method
+     */
+    public void doRollAdd(boolean roll, IBMCalendar cal, int[][] tests)
+    {
+        String name = roll ? "rolling" : "adding";
+        
+        for (int i = 0; i < tests.length; i++) {
+            int[] test = tests[i];
+
+            cal.clear();
+            cal.set(test[0], test[1], test[2]);
+            
+            if (roll) {
+                cal.roll(test[3], test[4]);
+            } else {
+                cal.add(test[3], test[4]);
+            }
+            
+            if (cal.get(YEAR) != test[5] || cal.get(MONTH) != test[6]
+                    || cal.get(DATE) != test[7])
+            {
+                errln("Error " + name + " "+ ymdToString(test[0], test[1], test[2])
+                    + " field " + test[3] + " by " + test[4]
+                    + ": expected " + ymdToString(test[5], test[6], test[7])
+                    + ", got " + ymdToString(cal.get(YEAR), cal.get(MONTH), cal.get(DATE)));
+            }
+        }
+    }
+
+    /**
+     * Convert year,month,day values to the form "year/month/day".
+     * On input the month value is zero-based, but in the result string it is one-based.
+     */
+    static public String ymdToString(int year, int month, int day) {
+        return "" + year + "/" + (month+1) + "/" + day;
+    }
+};
diff --git a/src/com/ibm/test/calendar/HebrewTest.java b/src/com/ibm/test/calendar/HebrewTest.java
new file mode 100755
index 0000000..4779f64
--- /dev/null
+++ b/src/com/ibm/test/calendar/HebrewTest.java
@@ -0,0 +1,202 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/calendar/Attic/HebrewTest.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.test.calendar;
+
+import com.ibm.test.*;
+import java.util.*;
+import java.text.*;
+import com.ibm.util.*;
+
+/**
+ * Tests for the <code>HebrewCalendar</code> class.
+ */
+public class HebrewTest extends CalendarTest {
+    public static void main(String args[]) throws Exception {
+        new HebrewTest().run(args);
+    }
+
+    // Constants to save typing.
+    public static final int TISHRI  = HebrewCalendar.TISHRI;
+    public static final int HESHVAN = HebrewCalendar.HESHVAN;
+    public static final int KISLEV  = HebrewCalendar.KISLEV;
+    public static final int TEVET   = HebrewCalendar.TEVET;
+    public static final int SHEVAT  = HebrewCalendar.SHEVAT;
+    public static final int ADAR_1  = HebrewCalendar.ADAR_1;
+    public static final int ADAR    = HebrewCalendar.ADAR;
+    public static final int NISAN   = HebrewCalendar.NISAN;
+    public static final int IYAR    = HebrewCalendar.IYAR;
+    public static final int SIVAN   = HebrewCalendar.SIVAN;
+    public static final int TAMUZ   = HebrewCalendar.TAMUZ;
+    public static final int AV      = HebrewCalendar.AV;
+    public static final int ELUL    = HebrewCalendar.ELUL;
+
+    /**
+     * Test the behavior of HebrewCalendar.roll
+     * The only real nastiness with roll is the MONTH field, since a year can
+     * have a variable number of months.
+     */
+    public void TestRoll() {
+        int[][] tests = new int[][] {
+            //       input                roll by          output
+            //  year  month     day     field amount    year  month     day
+    
+            {   5759, HESHVAN,   2,     MONTH,   1,     5759, KISLEV,    2 },   // non-leap years
+            {   5759, SHEVAT,    2,     MONTH,   1,     5759, ADAR,      2 },
+            {   5759, SHEVAT,    2,     MONTH,   2,     5759, NISAN,     2 },
+            {   5759, SHEVAT,    2,     MONTH,  12,     5759, SHEVAT,    2 },
+
+            {   5757, HESHVAN,   2,     MONTH,   1,     5757, KISLEV,    2 },   // leap years
+            {   5757, SHEVAT,    2,     MONTH,   1,     5757, ADAR_1,    2 },
+            {   5757, SHEVAT,    2,     MONTH,   2,     5757, ADAR,      2 },
+            {   5757, SHEVAT,    2,     MONTH,   3,     5757, NISAN,     2 },
+            {   5757, SHEVAT,    2,     MONTH,  12,     5757, TEVET,     2 },
+            {   5757, SHEVAT,    2,     MONTH,  13,     5757, SHEVAT,    2 },
+            
+            {   5757, KISLEV,    1,     DATE,   30,     5757, KISLEV,    2 },   // 29-day month
+            {   5758, KISLEV,    1,     DATE,   31,     5758, KISLEV,    2 },   // 30-day month
+            
+            // Try some other fields too
+            {   5757, TISHRI,    1,     YEAR,    1,     5758, TISHRI,    1 },
+   
+
+            // Try some rolls that require other fields to be adjusted
+            {   5757, TISHRI,   30,     MONTH,   1,     5757, HESHVAN,  29 },
+            {   5758, KISLEV,   30,     YEAR,   -1,     5757, KISLEV,   29 },
+        };
+       
+        HebrewCalendar cal = new HebrewCalendar(UTC, Locale.getDefault());
+
+        doRollAdd(ROLL, cal, tests);
+    }
+    
+    /**
+     * Test the behavior of HebrewCalendar.roll
+     * The only real nastiness with roll is the MONTH field, since a year can
+     * have a variable number of months.
+     */
+    public void TestAdd() {
+        int[][] tests = new int[][] {
+            //       input                add by          output
+            //  year  month     day     field amount    year  month     day
+            {   5759, HESHVAN,   2,     MONTH,   1,     5759, KISLEV,    2 },   // non-leap years
+            {   5759, SHEVAT,    2,     MONTH,   1,     5759, ADAR,      2 },
+            {   5759, SHEVAT,    2,     MONTH,   2,     5759, NISAN,     2 },
+            {   5759, SHEVAT,    2,     MONTH,  12,     5760, SHEVAT,    2 },
+
+            {   5757, HESHVAN,   2,     MONTH,   1,     5757, KISLEV,    2 },   // leap years
+            {   5757, SHEVAT,    2,     MONTH,   1,     5757, ADAR_1,    2 },
+            {   5757, SHEVAT,    2,     MONTH,   2,     5757, ADAR,      2 },
+            {   5757, SHEVAT,    2,     MONTH,   3,     5757, NISAN,     2 },
+            {   5757, SHEVAT,    2,     MONTH,  12,     5758, TEVET,     2 },
+            {   5757, SHEVAT,    2,     MONTH,  13,     5758, SHEVAT,    2 },
+            
+            {   5757, KISLEV,    1,     DATE,   30,     5757, TEVET,     2 },   // 29-day month
+            {   5758, KISLEV,    1,     DATE,   31,     5758, TEVET,     2 },   // 30-day month
+        };
+       
+        HebrewCalendar cal = new HebrewCalendar(UTC, Locale.getDefault());
+
+        doRollAdd(ADD, cal, tests);
+    }
+
+    /**
+     * A huge list of test cases to make sure that computeTime and computeFields
+     * work properly for a wide range of data.
+     */
+    public void TestCases() {
+        doTestCases(testCases, new HebrewCalendar());
+    }
+
+    static final TestCase[] testCases = {
+        //
+        // Most of these test cases were taken from the back of
+        // "Calendrical Calculations", with some extras added to help
+        // debug a few of the problems that cropped up in development.
+        //
+        // The months in this table are 1-based rather than 0-based,
+        // because it's easier to edit that way.
+        //
+        //         Julian Day  Era  Year  Month Day  WkDay Hour Min Sec
+        new TestCase(1507231.5,  0,  3174,   12,  10,  SUN,   0,  0,  0),
+        new TestCase(1660037.5,  0,  3593,    3,  25,  WED,   0,  0,  0),
+        new TestCase(1746893.5,  0,  3831,    1,   3,  WED,   0,  0,  0),
+        new TestCase(1770641.5,  0,  3896,    1,   9,  SUN,   0,  0,  0),
+        new TestCase(1892731.5,  0,  4230,    4,  18,  WED,   0,  0,  0),
+        new TestCase(1931579.5,  0,  4336,   10,   4,  MON,   0,  0,  0),
+        new TestCase(1974851.5,  0,  4455,    2,  13,  SAT,   0,  0,  0),
+        new TestCase(2091164.5,  0,  4773,    9,   6,  SUN,   0,  0,  0),
+        new TestCase(2121509.5,  0,  4856,    9,  23,  SUN,   0,  0,  0),
+        new TestCase(2155779.5,  0,  4950,    8,   7,  FRI,   0,  0,  0),
+        new TestCase(2174029.5,  0,  5000,    7,   8,  SAT,   0,  0,  0),
+        new TestCase(2191584.5,  0,  5048,    8,  21,  FRI,   0,  0,  0),
+        new TestCase(2195261.5,  0,  5058,    9,   7,  SUN,   0,  0,  0),
+        new TestCase(2229274.5,  0,  5151,   11,   1,  SUN,   0,  0,  0),
+        new TestCase(2245580.5,  0,  5196,    5,   7,  WED,   0,  0,  0),
+        new TestCase(2266100.5,  0,  5252,    8,   3,  SAT,   0,  0,  0),
+        new TestCase(2288542.5,  0,  5314,    1,   1,  SAT,   0,  0,  0),
+        new TestCase(2290901.5,  0,  5320,    6,  27,  SAT,   0,  0,  0),
+        new TestCase(2323140.5,  0,  5408,   10,  20,  WED,   0,  0,  0),
+        new TestCase(2334551.5,  0,  5440,    1,   1,  THU,   0,  0,  0),
+        new TestCase(2334581.5,  0,  5440,    2,   1,  SAT,   0,  0,  0),
+        new TestCase(2334610.5,  0,  5440,    3,   1,  SUN,   0,  0,  0),
+        new TestCase(2334639.5,  0,  5440,    4,   1,  MON,   0,  0,  0),
+        new TestCase(2334668.5,  0,  5440,    5,   1,  TUE,   0,  0,  0),
+        new TestCase(2334698.5,  0,  5440,    6,   1,  THU,   0,  0,  0),
+        new TestCase(2334728.5,  0,  5440,    7,   1,  SAT,   0,  0,  0),
+        new TestCase(2334757.5,  0,  5440,    8,   1,  SUN,   0,  0,  0),
+        new TestCase(2334787.5,  0,  5440,    9,   1,  TUE,   0,  0,  0),
+        new TestCase(2334816.5,  0,  5440,   10,   1,  WED,   0,  0,  0),
+        new TestCase(2334846.5,  0,  5440,   11,   1,  FRI,   0,  0,  0),
+        new TestCase(2334848.5,  0,  5440,   11,   3,  SUN,   0,  0,  0),
+        new TestCase(2334934.5,  0,  5441,    1,   1,  TUE,   0,  0,  0),
+        new TestCase(2348020.5,  0,  5476,   12,   5,  FRI,   0,  0,  0),
+        new TestCase(2366978.5,  0,  5528,   11,   4,  SUN,   0,  0,  0),
+        new TestCase(2385648.5,  0,  5579,   12,  11,  MON,   0,  0,  0),
+        new TestCase(2392825.5,  0,  5599,    8,  12,  WED,   0,  0,  0),
+        new TestCase(2416223.5,  0,  5663,    8,  22,  SUN,   0,  0,  0),
+        new TestCase(2425848.5,  0,  5689,   12,  19,  SUN,   0,  0,  0),
+        new TestCase(2430266.5,  0,  5702,    1,   8,  MON,   0,  0,  0),
+        new TestCase(2430833.5,  0,  5703,    8,  14,  MON,   0,  0,  0),
+        new TestCase(2431004.5,  0,  5704,    1,   8,  THU,   0,  0,  0),
+        new TestCase(2448698.5,  0,  5752,    7,  12,  TUE,   0,  0,  0),
+        new TestCase(2450138.5,  0,  5756,    7,   5,  SUN,   0,  0,  0),
+        new TestCase(2465737.5,  0,  5799,    2,  12,  WED,   0,  0,  0),
+        new TestCase(2486076.5,  0,  5854,   12,   5,  SUN,   0,  0,  0),
+
+        // Additional test cases for bugs found during development
+        //           G.YY/MM/DD  Era  Year  Month Day  WkDay Hour Min Sec
+        new TestCase(1013, 9, 8, 0,  4774,    1,   1,  TUE,   0,  0,  0),
+        new TestCase(1239, 9, 1, 0,  5000,    1,   1,  THU,   0,  0,  0),
+        new TestCase(1240, 9,18, 0,  5001,    1,   1,  TUE,   0,  0,  0),
+
+        // Test cases taken from a table of 14 "year types" in the Help file
+        // of the application "Hebrew Calendar"
+        new TestCase(2456187.5,  0,  5773,    1,   1,  MON,   0,  0,  0),
+        new TestCase(2459111.5,  0,  5781,    1,   1,  SAT,   0,  0,  0),
+        new TestCase(2453647.5,  0,  5766,    1,   1,  TUE,   0,  0,  0),
+        new TestCase(2462035.5,  0,  5789,    1,   1,  THU,   0,  0,  0),
+        new TestCase(2458756.5,  0,  5780,    1,   1,  MON,   0,  0,  0),
+        new TestCase(2460586.5,  0,  5785,    1,   1,  THU,   0,  0,  0),
+        new TestCase(2463864.5,  0,  5794,    1,   1,  SAT,   0,  0,  0),
+        new TestCase(2463481.5,  0,  5793,    1,   1,  MON,   0,  0,  0),
+        new TestCase(2470421.5,  0,  5812,    1,   1,  THU,   0,  0,  0),
+        new TestCase(2460203.5,  0,  5784,    1,   1,  SAT,   0,  0,  0),
+        new TestCase(2459464.5,  0,  5782,    1,   1,  TUE,   0,  0,  0),
+        new TestCase(2467142.5,  0,  5803,    1,   1,  MON,   0,  0,  0),
+        new TestCase(2455448.5,  0,  5771,    1,   1,  THU,   0,  0,  0),
+        new TestCase(2487223.5,  0,  5858,    1,   1,  SAT,   0,  0,  0),
+    };
+    
+    
+};
\ No newline at end of file
diff --git a/src/com/ibm/test/calendar/IslamicTest.java b/src/com/ibm/test/calendar/IslamicTest.java
new file mode 100755
index 0000000..66a29e2
--- /dev/null
+++ b/src/com/ibm/test/calendar/IslamicTest.java
@@ -0,0 +1,128 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/calendar/Attic/IslamicTest.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.calendar;
+
+import com.ibm.test.*;
+import java.util.*;
+import java.text.*;
+import com.ibm.util.*;
+
+/**
+ * Tests for the <code>IslamicCalendar</code> class.
+ */
+public class IslamicTest extends CalendarTest {
+    public static void main(String args[]) throws Exception {
+        new IslamicTest().run(args);
+    }
+
+    /** Constants to save typing. */
+    public static final int MUHARRAM = IslamicCalendar.MUHARRAM;
+    public static final int SAFAR =  IslamicCalendar.SAFAR;
+    public static final int RABI_1 =  IslamicCalendar.RABI_1;
+    public static final int RABI_2 =  IslamicCalendar.RABI_2;
+    public static final int JUMADA_1 =  IslamicCalendar.JUMADA_1;
+    public static final int JUMADA_2 =  IslamicCalendar.JUMADA_2;
+    public static final int RAJAB =  IslamicCalendar.RAJAB;
+    public static final int SHABAN =  IslamicCalendar.SHABAN;
+    public static final int RAMADAN =  IslamicCalendar.RAMADAN;
+    public static final int SHAWWAL =  IslamicCalendar.SHAWWAL;
+    public static final int QIDAH =  IslamicCalendar.DHU_AL_QIDAH;
+    public static final int HIJJAH =  IslamicCalendar.DHU_AL_HIJJAH;
+
+    public void TestRoll() {
+        int[][] tests = new int[][] {
+            //       input                roll by          output
+            //  year  month     day     field amount    year  month     day
+    
+            {   0001, QIDAH,     2,     MONTH,   1,     0001, HIJJAH,    2 },   // non-leap years
+            {   0001, QIDAH,     2,     MONTH,   2,     0001, MUHARRAM,  2 },
+            {   0001, QIDAH,     2,     MONTH,  -1,     0001, SHAWWAL,   2 },
+            {   0001, MUHARRAM,  2,     MONTH,  12,     0001, MUHARRAM,  2 },
+            {   0001, MUHARRAM,  2,     MONTH,  13,     0001, SAFAR,     2 },
+
+            {   0001, HIJJAH,    1,     DATE,   30,     0001, HIJJAH,    2 },   // 29-day month
+            {   0002, HIJJAH,    1,     DATE,   31,     0002, HIJJAH,    2 },   // 30-day month
+
+            // Try some rolls that require other fields to be adjusted
+            {   0001, MUHARRAM, 30,     MONTH,   1,     0001, SAFAR,    29 },
+            {   0002, HIJJAH,   30,     YEAR,   -1,     0001, HIJJAH,   29 },
+        };
+       
+        IslamicCalendar cal = newCivil();
+
+        doRollAdd(ROLL, cal, tests);
+    }
+
+    /**
+     * A huge list of test cases to make sure that computeTime and computeFields
+     * work properly for a wide range of data in the civil calendar.
+     */
+    public void TestCivilCases()
+    {
+        final TestCase[] tests = {
+            //
+            // Most of these test cases were taken from the back of
+            // "Calendrical Calculations", with some extras added to help
+            // debug a few of the problems that cropped up in development.
+            //
+            // The months in this table are 1-based rather than 0-based,
+            // because it's easier to edit that way.
+            //                       Islamic
+            //          Julian Day  Era  Year  Month Day  WkDay Hour Min Sec
+            new TestCase(1507231.5,  0, -1245,   12,   9,  SUN,   0,  0,  0),
+            new TestCase(1660037.5,  0,  -813,    2,  23,  WED,   0,  0,  0),
+            new TestCase(1746893.5,  0,  -568,    4,   1,  WED,   0,  0,  0),
+            new TestCase(1770641.5,  0,  -501,    4,   6,  SUN,   0,  0,  0),
+            new TestCase(1892731.5,  0,  -157,   10,  17,  WED,   0,  0,  0),
+            new TestCase(1931579.5,  0,   -47,    6,   3,  MON,   0,  0,  0),
+            new TestCase(1974851.5,  0,    75,    7,  13,  SAT,   0,  0,  0),
+            new TestCase(2091164.5,  0,   403,   10,   5,  SUN,   0,  0,  0),
+            new TestCase(2121509.5,  0,   489,    5,  22,  SUN,   0,  0,  0),
+            new TestCase(2155779.5,  0,   586,    2,   7,  FRI,   0,  0,  0),
+            new TestCase(2174029.5,  0,   637,    8,   7,  SAT,   0,  0,  0),
+            new TestCase(2191584.5,  0,   687,    2,  20,  FRI,   0,  0,  0),
+            new TestCase(2195261.5,  0,   697,    7,   7,  SUN,   0,  0,  0),
+            new TestCase(2229274.5,  0,   793,    7,   1,  SUN,   0,  0,  0),
+            new TestCase(2245580.5,  0,   839,    7,   6,  WED,   0,  0,  0),
+            new TestCase(2266100.5,  0,   897,    6,   1,  SAT,   0,  0,  0),
+            new TestCase(2288542.5,  0,   960,    9,  30,  SAT,   0,  0,  0),
+            new TestCase(2290901.5,  0,   967,    5,  27,  SAT,   0,  0,  0),
+            new TestCase(2323140.5,  0,  1058,    5,  18,  WED,   0,  0,  0),
+            new TestCase(2334848.5,  0,  1091,    6,   2,  SUN,   0,  0,  0),
+            new TestCase(2348020.5,  0,  1128,    8,   4,  FRI,   0,  0,  0),
+            new TestCase(2366978.5,  0,  1182,    2,   3,  SUN,   0,  0,  0),
+            new TestCase(2385648.5,  0,  1234,   10,  10,  MON,   0,  0,  0),
+            new TestCase(2392825.5,  0,  1255,    1,  11,  WED,   0,  0,  0),
+            new TestCase(2416223.5,  0,  1321,    1,  21,  SUN,   0,  0,  0),
+            new TestCase(2425848.5,  0,  1348,    3,  19,  SUN,   0,  0,  0),
+            new TestCase(2430266.5,  0,  1360,    9,   8,  MON,   0,  0,  0),
+            new TestCase(2430833.5,  0,  1362,    4,  13,  MON,   0,  0,  0),
+            new TestCase(2431004.5,  0,  1362,   10,   7,  THU,   0,  0,  0),
+            new TestCase(2448698.5,  0,  1412,    9,  13,  TUE,   0,  0,  0),
+            new TestCase(2450138.5,  0,  1416,   10,   5,  SUN,   0,  0,  0),
+            new TestCase(2465737.5,  0,  1460,   10,  12,  WED,   0,  0,  0),
+            new TestCase(2486076.5,  0,  1518,    3,   5,  SUN,   0,  0,  0),
+        };
+        
+        IslamicCalendar civilCalendar = newCivil();
+        civilCalendar.setLenient(true);
+        doTestCases(tests, civilCalendar);
+    }
+
+    private static IslamicCalendar newCivil() {
+        IslamicCalendar civilCalendar = new IslamicCalendar();
+        civilCalendar.setCivil(true);
+        return civilCalendar;
+    }
+    
+};
\ No newline at end of file
diff --git a/src/com/ibm/test/calendar/TestCase.java b/src/com/ibm/test/calendar/TestCase.java
new file mode 100755
index 0000000..65911ca
--- /dev/null
+++ b/src/com/ibm/test/calendar/TestCase.java
@@ -0,0 +1,214 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/calendar/Attic/TestCase.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.calendar;
+
+import com.ibm.test.*;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+import java.util.Locale;
+
+/**
+ * A dummy <code>Calendar</code> subclass that is useful for testing
+ * new calendars.  A <code>TestCase</code> object is used to hold the
+ * field and millisecond values that the calendar should have at one
+ * particular instant in time.  The applyFields and applyTime
+ * methods are used to apply these settings to the calendar object being
+ * tested, and the equals and fieldsEqual methods are used to ensure
+ * that the calendar has ended up in the right state.
+ */
+public class TestCase extends Calendar {
+    
+    /**
+     * Initialize a TestCase object using a julian day number and
+     * the corresponding fields for the calendar being tested.
+     *
+     * @param era       The ERA field of tested calendar on the given julian day
+     * @param year      The YEAR field of tested calendar on the given julian day
+     * @param month     The MONTH (0-based) field of tested calendar on the given julian day
+     * @param day       The DAY_OF_MONTH field of tested calendar on the given julian day
+     * @param dayOfWeek The DAY_OF_WEEK field of tested calendar on the given julian day
+     * @param hour      The HOUR field of tested calendar on the given julian day
+     * @param min       The MINUTE field of tested calendar on the given julian day
+     * @param sec       The SECOND field of tested calendar on the given julian day
+     */
+    public TestCase(double julian,
+                   int era, int year, int month, int day,
+                   int dayOfWeek,
+                   int hour, int min, int sec)
+    {
+        super(UTC, Locale.getDefault());
+        
+        setTime(new Date(JULIAN_EPOCH + (long)(DAY_MS * julian)));
+        
+        set(ERA, era);
+        set(YEAR, year);
+        set(MONTH, month - 1);
+        set(DATE, day);
+        set(DAY_OF_WEEK, dayOfWeek);
+        set(HOUR, hour);
+        set(MINUTE, min);
+        set(SECOND, sec);
+    }
+
+    /**
+     * Initialize a TestCase object using a Gregorian year/month/day and
+     * the corresponding fields for the calendar being tested.
+     *
+     * @param gregYear  The Gregorian year of the date to be tested
+     * @param gregMonth The Gregorian month of the date to be tested
+     * @param gregDay   The Gregorian day of the month of the date to be tested
+     *
+     * @param era       The ERA field of tested calendar on the given gregorian date
+     * @param year      The YEAR field of tested calendar on the given gregorian date
+     * @param month     The MONTH (0-based) field of tested calendar on the given gregorian date
+     * @param day       The DAY_OF_MONTH field of tested calendar on the given gregorian date
+     * @param dayOfWeek The DAY_OF_WEEK field of tested calendar on the given gregorian date
+     * @param hour      The HOUR field of tested calendar on the given gregorian date
+     * @param min       The MINUTE field of tested calendar on the given gregorian date
+     * @param sec       The SECOND field of tested calendar on the given gregorian date
+     */
+    public TestCase(int gregYear, int gregMonth, int gregDay,
+                   int era, int year, int month, int day,
+                   int dayOfWeek,
+                   int hour, int min, int sec)
+    {
+        super(UTC, Locale.getDefault());
+        
+        GregorianCalendar greg = new GregorianCalendar(UTC, Locale.getDefault());
+        greg.clear();
+        greg.set(gregYear, gregMonth-1, gregDay);
+        setTime(greg.getTime());
+        
+        set(ERA, era);
+        set(YEAR, year);
+        set(MONTH, month - 1);
+        set(DATE, day);
+        set(DAY_OF_WEEK, dayOfWeek);
+        set(HOUR, hour);
+        set(MINUTE, min);
+        set(SECOND, sec);
+    }
+    
+    /**
+     * Apply this test case's field values to another calendar
+     * by calling its set method for each field.  This is useful in combination
+     * with the equal method.
+     *
+     * @see #equal
+     */
+    public void applyFields(Calendar c) {
+        c.set(ERA,     fields[ERA]);
+        c.set(YEAR,    fields[YEAR]);
+        c.set(MONTH,   fields[MONTH]);
+        c.set(DATE,    fields[DATE]);
+        c.set(HOUR,    fields[HOUR]);
+        c.set(MINUTE,  fields[MINUTE]);
+        c.set(SECOND,  fields[SECOND]);
+    }
+    
+    /**
+     * Apply this test case's time in milliseconds to another calendar
+     * by calling its setTime method.  This is useful in combination
+     * with fieldsEqual
+     *
+     * @see #fieldsEqual
+     */
+    public void applyTime(Calendar c) {
+        c.setTime(new Date(time));
+    }
+
+    /**
+     * Determine whether the fields of this calendar
+     * are the same as that of the other calendar.  This method is useful
+     * for determining whether the other calendar's computeFields method
+     * works properly.  For example:
+     * <pre>
+     *    Calendar testCalendar = ...
+     *    TestCase case = ...
+     *    case.applyTime(testCalendar);
+     *    if (!case.fieldsEqual(testCalendar)) {
+     *        // Error!
+     *    }
+     * </pre>
+     * 
+     * @see #applyTime
+     */
+    public boolean fieldsEqual(Calendar c) {
+        for (int i=0; i < Calendar.FIELD_COUNT; i++) {
+            if (isSet(i) && get(i) != c.get(i)) {
+                System.out.println("field " + i + ": expected " + get(i) + ", got " + c.get(i));
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    /**
+     * Determine whether time in milliseconds of this calendar
+     * is the same as that of the other calendar.  This method is useful
+     * for determining whether the other calendar's computeTime method
+     * works properly.  For example:
+     * <pre>
+     *    Calendar testCalendar = ...
+     *    TestCase case = ...
+     *    case.applyFields(testCalendar);
+     *    if (!case.equals(testCalendar)) {
+     *        // Error!
+     *    }
+     * </pre>
+     * 
+     * @see #applyFields
+     */
+    public boolean equals(Object obj) {
+        return time == ((Calendar)obj).getTime().getTime();
+    }
+    
+    /**
+     * Determine whether time of this calendar (as returned by getTime)
+     * is before that of the specified calendar
+     */
+    public boolean before(Object obj) {
+        return time < ((Calendar)obj).getTime().getTime();
+    }
+
+    /**
+     * Determine whether time of this calendar (as returned by getTime)
+     * is after that of the specified calendar
+     */
+    public boolean after(Object obj) {
+        return time > ((Calendar)obj).getTime().getTime();
+    }
+    
+    // This object is only pretending to be a Calendar; it doesn't do any real
+    // calendar computatations.  But we have to pretend it does, because Calendar
+    // declares all of these abstract methods....
+    protected void computeTime() {}
+    protected void computeFields() {}
+    public void roll(int field, boolean up) {}
+    public void add(int field, int amt) {}
+    public int getMinimum(int field) { return 0; }
+    public int getMaximum(int field) { return 0; }
+    public int getGreatestMinimum(int field) { return 0; }
+    public int getLeastMaximum(int field) { return 0; }
+    
+    private static final int  SECOND_MS = 1000;
+    private static final int  MINUTE_MS = 60*SECOND_MS;
+    private static final int  HOUR_MS   = 60*MINUTE_MS;
+    private static final long DAY_MS    = 24*HOUR_MS;
+    private static final long JULIAN_EPOCH = -210866760000000L;   // 1/1/4713 BC 12:00
+
+    public final static SimpleTimeZone UTC = new SimpleTimeZone(0, "GMT");
+}
diff --git a/src/com/ibm/test/compression/DecompressionTest.java b/src/com/ibm/test/compression/DecompressionTest.java
new file mode 100755
index 0000000..147f216
--- /dev/null
+++ b/src/com/ibm/test/compression/DecompressionTest.java
@@ -0,0 +1,130 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/compression/Attic/DecompressionTest.java,v $ 
+ * $Date: 2000/03/10 03:47:45 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.compression;
+
+import com.ibm.text.UnicodeCompressor;
+import com.ibm.text.UnicodeDecompressor;
+import com.ibm.test.TestFmwk;
+
+public class DecompressionTest extends TestFmwk {
+	public static void main(String[] args) throws Exception {
+		new DecompressionTest().run(args);
+	}
+
+    /** Print out a segment of a character array, if in verbose mode */
+    private void log(char [] chars, int start, int count) {
+        log("|");
+        for(int i = start; i < start + count; ++i) {
+            log(String.valueOf(chars[i]));
+        }
+        log("|");
+    }
+
+    /** Print out a segment of a character array, followed by a newline */
+    private void logln(char [] chars, int start, int count)
+    {
+        log(chars, start, count);
+        logln("");
+    }
+
+    /** Decompress the two segments */
+    private String decompressTest(byte [] segment1, byte [] segment2) {
+        StringBuffer s = new StringBuffer();
+        UnicodeDecompressor myDecompressor = new UnicodeDecompressor();
+
+        int [] bytesRead = new int[1];
+        char [] charBuffer = new char [2*(segment1.length + segment2.length)];
+        int count1 = 0, count2 = 0;
+
+        count1 = myDecompressor.decompress(segment1, 0, segment1.length,
+                                           bytesRead,
+                                           charBuffer, 0, charBuffer.length);
+        
+        logln("Segment 1 (" + segment1.length + " bytes) " +
+                "decompressed into " + count1  + " chars");
+        logln("Bytes consumed: " + bytesRead[0]);
+
+        logln("Got chars: ");
+        logln(charBuffer, 0, count1);
+        s.append(charBuffer, 0, count1);
+
+        count2 = myDecompressor.decompress(segment2, 0, segment2.length,
+                                           bytesRead,
+                                           charBuffer, count1, 
+                                           charBuffer.length);
+        
+        logln("Segment 2 (" + segment2.length + " bytes) " +
+                "decompressed into " + count2  + " chars");
+        logln("Bytes consumed: " + bytesRead[0]);
+
+        logln("Got chars: ");
+        logln(charBuffer, count1, count2);
+        
+        s.append(charBuffer, count1, count2);
+
+        logln("Result: ");
+        logln(charBuffer, 0, count1 + count2);
+        logln("====================");
+
+        return s.toString();
+    }
+
+
+    public void testDecompression() throws Exception {
+        String result;
+
+        // compressed segment breaking on a define window sequence
+        /*                   B     o     o     t     h     SD1  */
+        byte [] segment1 = { 0x42, 0x6f, 0x6f, 0x74, 0x68, 0x19 };
+
+        // continuation
+        /*                   IDX   ,           S     .          */
+        byte [] segment2 = { 0x01, 0x2c, 0x20, 0x53, 0x2e };
+        
+        result = decompressTest(segment1, segment2);
+        if(! result.equals("Booth, S.")) {
+        	errln("Decompression test failed");
+        	return;
+		}
+
+        // compressed segment breaking on a quote unicode sequence
+        /*                   B     o     o     t     SQU        */
+        byte [] segment3 = { 0x42, 0x6f, 0x6f, 0x74, 0x0e, 0x00 };
+
+        // continuation
+        /*                   h     ,           S     .          */
+        byte [] segment4 = { 0x68, 0x2c, 0x20, 0x53, 0x2e };
+
+        result = decompressTest(segment3, segment4);
+        if(! result.equals("Booth, S.")) {
+        	errln("Decompression test failed");
+        	return;
+		}
+
+
+        // compressed segment breaking on a quote unicode sequence
+        /*                   SCU   UQU                         */
+        byte [] segment5 = { 0x0f, (byte)0xf0, 0x00 };
+
+        // continuation
+        /*                   B                                 */
+        byte [] segment6 = { 0x42 };
+
+        result = decompressTest(segment5, segment6);
+        if(! result.equals("B")) {
+        	errln("Decompression test failed");
+        	return;
+		}
+    }
+
+};
diff --git a/src/com/ibm/test/compression/ExhaustiveTest.java b/src/com/ibm/test/compression/ExhaustiveTest.java
new file mode 100755
index 0000000..0a2c399
--- /dev/null
+++ b/src/com/ibm/test/compression/ExhaustiveTest.java
@@ -0,0 +1,529 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/compression/Attic/ExhaustiveTest.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.compression;
+
+import com.ibm.text.UnicodeCompressor;
+import com.ibm.text.UnicodeDecompressor;
+import java.util.Random;
+import com.ibm.test.TestFmwk;
+
+public class ExhaustiveTest extends TestFmwk {
+	public static void main(String args[]) throws Exception {
+		new ExhaustiveTest().run(args);
+	}
+
+    /** Test simple compress/decompress API, returning # of errors */
+	public void testSimple() throws Exception {
+        for(int i = 0; i < fTestCases.length; i++) {
+            simpleTest(fTestCases[i]);
+        }
+	}
+    private void simpleTest(String s) throws Exception {
+        byte [] compressed = UnicodeCompressor.compress(s);
+        String res = UnicodeDecompressor.decompress(compressed);
+        if (logDiffs(s.toCharArray(), s.length(), 
+                res.toCharArray(), res.length()) == false) {
+            logln(s.length() + " chars ===> " 
+                    + compressed.length + " bytes ===> " 
+                    + res.length() + " chars");
+        } else {
+            logln("Compressed:");
+            printBytes(compressed, compressed.length);
+            errln("testSimple did not compress correctly");
+        }
+    }
+
+    /** Test iterative compress/decompress API, returning # of errors */
+    public void testIterative() throws Exception {
+        for(int i = 0; i < fTestCases.length; i++) {
+            myTest(fTestCases[i].toCharArray(), fTestCases[i].length());
+        }
+    }
+    private void myTest(char[] chars, int len) {
+        UnicodeCompressor myCompressor = new UnicodeCompressor();
+        UnicodeDecompressor myDecompressor = new UnicodeDecompressor();
+        
+        // variables for my compressor
+        int myByteCount = 0;
+        int myCharCount = 0;
+        int myCompressedSize = Math.max(512, 3*len);
+        byte[] myCompressed = new byte[myCompressedSize];
+        int myDecompressedSize = Math.max(2, 2 * len);
+        char[] myDecompressed = new char[myDecompressedSize];
+        int[] unicharsRead = new int[1];
+        int[] bytesRead = new int[1];
+        
+        myByteCount = myCompressor.compress(chars, 0, len, unicharsRead,
+                myCompressed, 0, myCompressedSize);
+
+        myCharCount = myDecompressor.decompress(myCompressed, 0, myByteCount,
+                bytesRead, myDecompressed, 0, myDecompressedSize);
+
+        if (logDiffs(chars, len, myDecompressed, myCharCount) == false) {
+            logln(len + " chars ===> " 
+                    + myByteCount + " bytes ===> " 
+                    + myCharCount + " chars");
+        } else {
+            logln("Compressed:");
+            printBytes(myCompressed, myByteCount);
+            errln("Iterative test failed");
+        }
+    }
+
+    /** Test iterative compress/decompress API */
+    public void testMultipass() throws Exception {
+        for(int i = 0; i < fTestCases.length; i++) {
+            myMultipassTest(fTestCases[i].toCharArray(), fTestCases[i].length());
+        }
+    }
+    private void myMultipassTest(char [] chars, int len) throws Exception {
+        UnicodeCompressor myCompressor = new UnicodeCompressor();
+        UnicodeDecompressor myDecompressor = new UnicodeDecompressor();
+        
+        // variables for my compressor
+        
+        // for looping
+        int byteBufferSize = 4;//Math.max(4, len / 4);
+        byte[] byteBuffer = new byte [byteBufferSize];
+        // real target
+        int compressedSize = Math.max(512, 3 * len);
+        byte[] compressed = new byte[compressedSize];
+
+        // for looping
+        int unicharBufferSize = 2;//byteBufferSize;
+        char[] unicharBuffer = new char[unicharBufferSize];
+        // real target
+        int decompressedSize = Math.max(2, 2 * len);
+        char[] decompressed = new char[decompressedSize];
+
+        int bytesWritten = 0;
+        int unicharsWritten = 0;
+
+        int[] unicharsRead = new int[1];
+        int[] bytesRead = new int[1];
+        
+        int totalCharsCompressed = 0;
+        int totalBytesWritten = 0;
+
+        int totalBytesDecompressed  = 0;
+        int totalCharsWritten = 0;
+
+        boolean err = false;
+
+
+        // perform the compression in a loop
+        do {
+            
+            // do the compression
+            bytesWritten = myCompressor.compress(chars, totalCharsCompressed, 
+                   len, unicharsRead, byteBuffer, 0, byteBufferSize);
+
+            // copy the current set of bytes into the target buffer
+            System.arraycopy(byteBuffer, 0, compressed, 
+                   totalBytesWritten, bytesWritten);
+            
+            // update the no. of characters compressed
+            totalCharsCompressed += unicharsRead[0];
+            
+            // update the no. of bytes written
+            totalBytesWritten += bytesWritten;
+            
+            /*System.out.logln("Compression pass complete.  Compressed "
+                               + unicharsRead[0] + " chars into "
+                               + bytesWritten + " bytes.");*/
+        } while(totalCharsCompressed < len);
+
+        if (totalCharsCompressed != len) {
+            errln("ERROR: Number of characters compressed("
+                    + totalCharsCompressed + ") != len(" + len + ")");
+        } else {
+            logln("MP: " + len + " chars ===> " + totalBytesWritten + " bytes.");
+        }
+        
+        // perform the decompression in a loop
+        do {
+            
+            // do the decompression
+            unicharsWritten = myDecompressor.decompress(compressed, 
+                    totalBytesDecompressed, totalBytesWritten, 
+                    bytesRead, unicharBuffer, 0, unicharBufferSize);
+
+            // copy the current set of chars into the target buffer
+            System.arraycopy(unicharBuffer, 0, decompressed, 
+                    totalCharsWritten, unicharsWritten);
+            
+            // update the no. of bytes decompressed
+            totalBytesDecompressed += bytesRead[0];
+            
+            // update the no. of chars written
+            totalCharsWritten += unicharsWritten;
+            
+            /*System.out.logln("Decompression pass complete.  Decompressed "
+                               + bytesRead[0] + " bytes into "
+                               + unicharsWritten + " chars.");*/
+        } while (totalBytesDecompressed < totalBytesWritten);
+
+        if (totalBytesDecompressed != totalBytesWritten) {
+            errln("ERROR: Number of bytes decompressed(" 
+                    + totalBytesDecompressed 
+                    + ") != totalBytesWritten(" 
+                    + totalBytesWritten + ")");
+        } else {
+            logln("MP: " + totalBytesWritten
+                    + " bytes ===> " + totalCharsWritten + " chars.");
+        }
+        
+        if (logDiffs(chars, len, decompressed, totalCharsWritten)) {
+            errln("ERROR: buffer contents incorrect");
+        }
+    }
+
+    /** Print differences between two character buffers */
+    private boolean logDiffs(char[] s1, int s1len, char[] s2, int s2len) {
+        boolean result  = false;
+        
+        if(s1len != s2len) {
+            logln("====================");
+            logln("Length doesn't match: expected " + s1len
+                               + ", got " + s2len);
+            logln("Expected:");
+            printChars(s1, s1len);
+            logln("Got:");
+            printChars(s2, s2len);
+            result = true;
+        }
+        
+        int len = Math.min(s1len, s2len);
+        for(int i = 0; i < len; ++i) {
+            if(s1[i] != s2[i]) {
+                if(result == false) {
+                    logln("====================");
+                }
+                logln("First difference at char " + i);
+                logln("Exp. char: " + Integer.toHexString(s1[i]));
+                logln("Got char : " + Integer.toHexString(s2[i]));
+                logln("Expected:");
+                printChars(s1, s1len);
+                logln("Got:");
+                printChars(s2, s2len);
+                result = true;
+                break;
+            }
+        }
+    
+        return result;
+    }
+
+    // generate a string of characters, with simulated runs of characters
+    private static char[] randomChars(int len, Random random) {
+        char[] result = new char [len];
+        int runLen = 0;
+        int used = 0;
+        
+        while(used < len) {
+            runLen = (int) (30 * random.nextDouble());
+            if(used + runLen >= len) {
+                runLen = len - used;
+            }
+            randomRun(result, used, runLen, random);
+            used += runLen;
+        }
+    
+        return result;
+    }
+
+    // generate a run of characters in a "window"
+    private static void randomRun(char[] target, int pos, int len, Random random) {
+        int offset = (int) (0xFFFF * random.nextDouble());
+
+        // don't overflow 16 bits
+        if(offset > 0xFF80) {
+            offset = 0xFF80;
+        }
+
+        for(int i = pos; i < pos + len; i++) {
+            target[i] = (char)(offset + (0x7F * random.nextDouble()));
+        }
+    }
+
+    private static final String [] fTestCases = {
+        "Hello \u9292 \u9192 World!",
+        "Hell\u0429o \u9292 \u9192 W„rld!",
+        "Hell\u0429o \u9292 \u9292W„rld!",
+
+        "\u0648\u06c8", // catch missing reset
+        "\u0648\u06c8",
+
+        "\u4444\uE001", // lowest quotable
+        "\u4444\uf2FF", // highest quotable
+        "\u4444\uf188\u4444",
+        "\u4444\uf188\uf288",
+        "\u4444\uf188abc\0429\uf288",
+        "\u9292\u2222",
+        "Hell\u0429\u04230o \u9292 \u9292W„\u0192rld!",
+        "Hell\u0429o \u9292 \u9292W„rld!",
+        "Hello World!123456",
+        "Hello W\u0081\u011f\u0082!", // Latin 1 run
+
+        "abc\u0301\u0302",  // uses SQn for u301 u302
+        "abc\u4411d",      // uses SQU
+        "abc\u4411\u4412d",// uses SCU
+        "abc\u0401\u0402\u047f\u00a5\u0405", // uses SQn for ua5
+        "\u9191\u9191\u3041\u9191\u3041\u3041\u3000", // SJIS like data
+        "\u9292\u2222",
+        "\u9191\u9191\u3041\u9191\u3041\u3041\u3000",
+        "\u9999\u3051\u300c\u9999\u9999\u3060\u9999\u3065\u3065\u3065\u300c",
+        "\u3000\u266a\u30ea\u30f3\u30b4\u53ef\u611b\u3044\u3084\u53ef\u611b\u3044\u3084\u30ea\u30f3\u30b4\u3002",
+
+        "", // empty input
+        "\u0000", // smallest BMP character
+        "\uFFFF", // largest BMP character
+
+        "\ud800\udc00", // smallest surrogate
+        "\ud8ff\udcff", // largest surrogate pair
+        
+        // regression tests
+        "\u6441\ub413\ua733\uf8fe\ueedb\u587f\u195f\u4899\uf23d\u49fd\u0aac\u5792\ufc22\ufc3c\ufc46\u00aa",
+        "\u30f9\u8321\u05e5\u181c\ud72b\u2019\u99c9\u2f2f\uc10c\u82e1\u2c4d\u1ebc\u6013\u66dc\ubbde\u94a5\u4726\u74af\u3083\u55b9\u000c",
+        "\u0041\u00df\u0401\u015f",
+        "\u9066\u2123abc",
+        "\ud266\u43d7\\\ue386\uc9c0\u4a6b\u9222\u901f\u7410\ua63f\u539b\u9596\u482e\u9d47\ucfe4\u7b71\uc280\uf26a\u982f\u862a\u4edd\uf513\ufda6\u869d\u2ee0\ua216\u3ff6\u3c70\u89c0\u9576\ud5ec\ubfda\u6cca\u5bb3\ubcea\u554c\u914e\ufa4a\uede3\u2990\ud2f5\u2729\u5141\u0f26\uccd8\u5413\ud196\ubbe2\u51b9\u9b48\u0dc8\u2195\u21a2\u21e9\u00e4\u9d92\u0bc0\u06c5",
+        "\uf95b\u2458\u2468\u0e20\uf51b\ue36e\ubfc1\u0080\u02dd\uf1b5\u0cf3\u6059\u7489"
+
+    };
+
+    //==========================
+    // Compression modes
+    //==========================
+    private final static int SINGLEBYTEMODE                 = 0;
+    private final static int UNICODEMODE                        = 1;
+    
+    //==========================
+    // Single-byte mode tags
+    //==========================
+    private final static int SDEFINEX                   = 0x0B;
+    private final static int SRESERVED                  = 0x0C;             // this is a reserved value
+    private final static int SQUOTEU                    = 0x0E;
+    private final static int SSWITCHU                   = 0x0F;
+
+        private final static int SQUOTE0                        = 0x01;
+        private final static int SQUOTE1                        = 0x02;
+        private final static int SQUOTE2                        = 0x03;
+        private final static int SQUOTE3                        = 0x04;
+        private final static int SQUOTE4                        = 0x05;
+        private final static int SQUOTE5                        = 0x06;
+        private final static int SQUOTE6                            = 0x07;
+        private final static int SQUOTE7                        = 0x08;
+
+        private final static int SSWITCH0                       = 0x10;
+        private final static int SSWITCH1                       = 0x11;
+        private final static int SSWITCH2                       = 0x12;
+        private final static int SSWITCH3                       = 0x13;
+        private final static int SSWITCH4                       = 0x14;
+        private final static int SSWITCH5                       = 0x15;
+        private final static int SSWITCH6                       = 0x16;
+        private final static int SSWITCH7                       = 0x17;
+
+        private final static int SDEFINE0                       = 0x18;
+        private final static int SDEFINE1                       = 0x19;
+        private final static int SDEFINE2                       = 0x1A;
+        private final static int SDEFINE3                       = 0x1B;
+        private final static int SDEFINE4                       = 0x1C;
+        private final static int SDEFINE5                       = 0x1D;
+        private final static int SDEFINE6                       = 0x1E;
+        private final static int SDEFINE7                       = 0x1F;
+
+        //==========================
+        // Unicode mode tags
+        //==========================
+        private final static int USWITCH0                       = 0xE0;
+        private final static int USWITCH1                       = 0xE1;
+        private final static int USWITCH2                       = 0xE2;
+        private final static int USWITCH3                       = 0xE3;
+        private final static int USWITCH4                       = 0xE4;
+        private final static int USWITCH5                       = 0xE5;
+        private final static int USWITCH6                       = 0xE6;
+        private final static int USWITCH7                       = 0xE7;
+
+        private final static int UDEFINE0                       = 0xE8;
+        private final static int UDEFINE1                       = 0xE9;
+        private final static int UDEFINE2                       = 0xEA;
+        private final static int UDEFINE3                       = 0xEB;
+        private final static int UDEFINE4                       = 0xEC;
+        private final static int UDEFINE5                       = 0xED;
+        private final static int UDEFINE6                       = 0xEE;
+        private final static int UDEFINE7                       = 0xEF;
+
+        private final static int UQUOTEU                        = 0xF0;
+        private final static int UDEFINEX                       = 0xF1;
+        private final static int URESERVED                      = 0xF2;         // this is a reserved value
+
+    /* Print out an array of characters, with non-printables (for me) 
+       displayed as hex values */
+    private void printChars(char[] chars, int len) {
+        for(int i = 0; i < len; i++) {
+            int c = (int)chars[i];
+            if(c < 0x0020 || c >= 0x7f) {
+                log("[0x");
+                log(Integer.toHexString(c));
+                log("]");
+            } else {
+                log(String.valueOf((char)c));
+            }
+        }
+        logln("");
+    }
+
+    private void printBytes(byte[] byteBuffer, int len) {
+        int curByteIndex = 0;
+        int byteBufferLimit = len;
+        int mode = SINGLEBYTEMODE;
+        int aByte = 0x00;
+        
+        if(len > byteBuffer.length) {
+            logln("Warning: printBytes called with length too large. Truncating");
+            byteBufferLimit = byteBuffer.length;;
+        }
+        
+        while(curByteIndex < byteBufferLimit) {
+            switch(mode) {  
+            case SINGLEBYTEMODE:
+                while(curByteIndex < byteBufferLimit 
+                      && mode == SINGLEBYTEMODE)  {
+                    aByte = ((int)byteBuffer[curByteIndex++]) & 0xFF;
+                    switch(aByte) {
+                    default:
+                        log(Integer.toHexString(((int) aByte) & 0xFF) + " ");
+                        break;
+                        // quote unicode
+                    case SQUOTEU:
+                        log("SQUOTEU ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                        // switch to Unicode mode
+                    case SSWITCHU:
+                        log("SSWITCHU ");
+                        mode = UNICODEMODE;
+                        break;
+                        
+                        // handle all quote tags
+                    case SQUOTE0: case SQUOTE1: case SQUOTE2: case SQUOTE3:
+                    case SQUOTE4: case SQUOTE5: case SQUOTE6: case SQUOTE7:
+                        log("SQUOTE" + (aByte - SQUOTE0) + " ");
+                        if(curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                        // handle all switch tags
+                    case SSWITCH0: case SSWITCH1: case SSWITCH2: case SSWITCH3:
+                    case SSWITCH4: case SSWITCH5: case SSWITCH6: case SSWITCH7:
+                        log("SSWITCH" + (aByte - SSWITCH0) + " ");
+                        break;
+                                        
+                        // handle all define tags
+                    case SDEFINE0: case SDEFINE1: case SDEFINE2: case SDEFINE3:
+                    case SDEFINE4: case SDEFINE5: case SDEFINE6: case SDEFINE7:
+                        log("SDEFINE" + (aByte - SDEFINE0) + " ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                        // handle define extended tag
+                    case SDEFINEX:
+                        log("SDEFINEX ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                    } // end switch
+                } // end while
+                break;
+                
+            case UNICODEMODE:
+                while(curByteIndex < byteBufferLimit && mode == UNICODEMODE) {
+                    aByte = ((int)byteBuffer[curByteIndex++]) & 0xFF;
+                    switch(aByte) {
+                        // handle all define tags
+                    case UDEFINE0: case UDEFINE1: case UDEFINE2: case UDEFINE3:
+                    case UDEFINE4: case UDEFINE5: case UDEFINE6: case UDEFINE7:
+                        log("UDEFINE" + (aByte - UDEFINE0) + " ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        mode = SINGLEBYTEMODE;
+                        break;
+                        
+                        // handle define extended tag
+                    case UDEFINEX:
+                        log("UDEFINEX ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                        // handle all switch tags
+                    case USWITCH0: case USWITCH1: case USWITCH2: case USWITCH3:
+                    case USWITCH4: case USWITCH5: case USWITCH6: case USWITCH7:
+                        log("USWITCH" + (aByte - USWITCH0) + " ");
+                        mode = SINGLEBYTEMODE;
+                        break;
+                        
+                        // quote unicode
+                    case UQUOTEU:
+                        log("UQUOTEU ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                    default:
+                        log(Integer.toHexString(((int) aByte) & 0xFF) + " ");
+                        if (curByteIndex < byteBufferLimit) {
+                            log(Integer.toHexString(((int) byteBuffer[curByteIndex++]) & 0xFF) + " ");
+                        }
+                        break;
+                        
+                    } // end switch
+                } // end while
+                break;
+                
+            } // end switch( mode )
+        } // end while
+        
+        logln("");
+    }    
+}
+
+
+
+
+
+
diff --git a/src/com/ibm/test/normalizer/BasicTest.java b/src/com/ibm/test/normalizer/BasicTest.java
new file mode 100755
index 0000000..e732968
--- /dev/null
+++ b/src/com/ibm/test/normalizer/BasicTest.java
@@ -0,0 +1,340 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/normalizer/Attic/BasicTest.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.normalizer;
+
+import com.ibm.test.*;
+import com.ibm.text.*;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+
+public class BasicTest extends TestFmwk {
+    public static void main(String[] args) throws Exception {
+        new BasicTest().run(args);
+    }
+
+    String[][] canonTests = {
+        // Input                Decomposed              Composed
+        { "cat",                "cat",                  "cat"               },
+        { "\u00e0ardvark",      "a\u0300ardvark",       "\u00e0ardvark",    },
+
+        { "\u1e0a",             "D\u0307",              "\u1e0a"            }, // D-dot_above
+        { "D\u0307",            "D\u0307",              "\u1e0a"            }, // D dot_above
+
+        { "\u1e0c\u0307",       "D\u0323\u0307",        "\u1e0c\u0307"      }, // D-dot_below dot_above
+        { "\u1e0a\u0323",       "D\u0323\u0307",        "\u1e0c\u0307"      }, // D-dot_above dot_below
+        { "D\u0307\u0323",      "D\u0323\u0307",        "\u1e0c\u0307"      }, // D dot_below dot_above
+
+        { "\u1e10\u0307\u0323", "D\u0327\u0323\u0307",  "\u1e10\u0323\u0307"}, // D dot_below cedilla dot_above
+        { "D\u0307\u0328\u0323","D\u0328\u0323\u0307",  "\u1e0c\u0328\u0307"}, // D dot_above ogonek dot_below
+
+        { "\u1E14",             "E\u0304\u0300",        "\u1E14"            }, // E-macron-grave
+        { "\u0112\u0300",       "E\u0304\u0300",        "\u1E14"            }, // E-macron + grave
+        { "\u00c8\u0304",       "E\u0300\u0304",        "\u00c8\u0304"      }, // E-grave + macron
+
+        { "\u212b",             "A\u030a",              "\u00c5"            }, // angstrom_sign
+        { "\u00c5",             "A\u030a",              "\u00c5"            }, // A-ring
+
+//        { "ýffin",              "A\u0308ffin",          "ýffin"             },
+        { "ýffin",              "y\u0301ffin",          "ýffin"             },	//updated with 3.0
+//        { "ý\uFB03n",           "A\u0308\uFB03n",       "ý\uFB03n"          },
+        { "ý\uFB03n",           "y\u0301\uFB03n",       "ý\uFB03n"          },	//updated with 3.0
+
+        { "Henry IV",           "Henry IV",             "Henry IV"          },
+        { "Henry \u2163",       "Henry \u2163",         "Henry \u2163"      },
+
+        { "\u30AC",             "\u30AB\u3099",         "\u30AC"            }, // ga (Katakana)
+        { "\u30AB\u3099",       "\u30AB\u3099",         "\u30AC"            }, // ka + ten
+        { "\uFF76\uFF9E",       "\uFF76\uFF9E",         "\uFF76\uFF9E"      }, // hw_ka + hw_ten
+        { "\u30AB\uFF9E",       "\u30AB\uFF9E",         "\u30AB\uFF9E"      }, // ka + hw_ten
+        { "\uFF76\u3099",       "\uFF76\u3099",         "\uFF76\u3099"      }, // hw_ka + ten
+    };
+
+    String[][] compatTests = {
+            // Input                Decomposed              Composed
+        { "\uFB4f",             "\u05D0\u05DC",         "\u05D0\u05DC",     }, // Alef-Lamed vs. Alef, Lamed
+
+//        { "ýffin",              "A\u0308ffin",          "ýffin"             },
+//       { "ý\uFB03n",           "A\u0308ffin",          "ýffin"             }, // ffi ligature -> f + f + i
+        { "ýffin",              "y\u0301ffin",          "ýffin"             },	//updated for 3.0
+        { "ý\uFB03n",           "y\u0301ffin",          "ýffin"             }, // ffi ligature -> f + f + i
+
+        { "Henry IV",           "Henry IV",             "Henry IV"          },
+        { "Henry \u2163",       "Henry IV",             "Henry IV"          },
+
+        { "\u30AC",             "\u30AB\u3099",         "\u30AC"            }, // ga (Katakana)
+        { "\u30AB\u3099",       "\u30AB\u3099",         "\u30AC"            }, // ka + ten
+
+        { "\uFF76\u3099",       "\u30AB\u3099",         "\u30AC"            }, // hw_ka + ten
+
+        /* These two are broken in Unicode 2.1.2 but fixed in 2.1.5 and later
+        { "\uFF76\uFF9E",       "\u30AB\u3099",         "\u30AC"            }, // hw_ka + hw_ten
+        { "\u30AB\uFF9E",       "\u30AB\u3099",         "\u30AC"            }, // ka + hw_ten
+        */
+    };
+
+    // With Canonical decomposition, Hangul syllables should get decomposed
+    // into Jamo, but Jamo characters should not be decomposed into
+    // conjoining Jamo
+    String[][] hangulCanon = {
+        // Input                Decomposed              Composed
+        { "\ud4db",             "\u1111\u1171\u11b6",   "\ud4db"        },
+        { "\u1111\u1171\u11b6", "\u1111\u1171\u11b6",   "\ud4db"        },
+    };
+
+    // With compatibility decomposition turned on,
+    // it should go all the way down to conjoining Jamo characters.
+    // THIS IS NO LONGER TRUE IN UNICODE v2.1.8, SO THIS TEST IS OBSOLETE
+    String[][] hangulCompat = {
+        // Input        Decomposed                          Composed
+        // { "\ud4db",     "\u1111\u116e\u1175\u11af\u11c2",   "\ud478\u1175\u11af\u11c2"  },
+    };
+
+    public void TestHangulCompose() {
+        // Make sure that the static composition methods work
+        logln("Canonical composition...");
+        staticTest(Normalizer.COMPOSE,        0, hangulCanon,  2);
+        logln("Compatibility composition...");
+        staticTest(Normalizer.COMPOSE_COMPAT, 0, hangulCompat, 2);
+
+        // Now try iterative composition....
+        logln("Static composition...");
+        Normalizer norm = new Normalizer("", Normalizer.COMPOSE, 0);
+        iterateTest(norm, hangulCanon, 2);
+
+        norm.setMode(Normalizer.COMPOSE_COMPAT);
+        iterateTest(norm, hangulCompat, 2);
+
+        // And finally, make sure you can do it in reverse too
+        logln("Reverse iteration...");
+        norm.setMode(Normalizer.COMPOSE);
+        backAndForth(norm, hangulCanon);
+    }
+
+    public void TestHangulDecomp() {
+        // Make sure that the static decomposition methods work
+        logln("Canonical decomposition...");
+        staticTest(Normalizer.DECOMP,        0, hangulCanon,  1);
+        logln("Compatibility decomposition...");
+        staticTest(Normalizer.DECOMP_COMPAT, 0, hangulCompat, 1);
+
+        // Now the iterative decomposition methods...
+        logln("Iterative decomposition...");
+        Normalizer norm = new Normalizer("", Normalizer.DECOMP, 0);
+        iterateTest(norm, hangulCanon, 1);
+
+        norm.setMode(Normalizer.DECOMP_COMPAT);
+        iterateTest(norm, hangulCompat, 1);
+
+        // And finally, make sure you can do it in reverse too
+        logln("Reverse iteration...");
+        norm.setMode(Normalizer.DECOMP);
+        backAndForth(norm, hangulCanon);
+    }
+
+    public void TestPrevious() {
+        Normalizer norm = new Normalizer("", Normalizer.DECOMP, 0);
+
+        logln("testing decomp...");
+        backAndForth(norm, canonTests);
+
+        logln("testing compose...");
+        norm.setMode(Normalizer.COMPOSE);
+        backAndForth(norm, canonTests);
+    }
+
+    public void TestDecomp() {
+        Normalizer norm = new Normalizer("", Normalizer.DECOMP, 0);
+        iterateTest(norm, canonTests, 1);
+
+        staticTest(Normalizer.DECOMP, 0, canonTests, 1);
+    }
+
+    public void TestCompatDecomp() {
+        Normalizer norm = new Normalizer("", Normalizer.DECOMP_COMPAT, 0);
+        iterateTest(norm, compatTests, 1);
+
+        staticTest(Normalizer.DECOMP_COMPAT, 0, compatTests, 1);
+    }
+
+    public void TestCanonCompose() {
+        Normalizer norm = new Normalizer("", Normalizer.COMPOSE, 0);
+        iterateTest(norm, canonTests, 2);
+
+        staticTest(Normalizer.COMPOSE, 0, canonTests, 2);
+    }
+
+    public void TestCompatCompose() {
+        Normalizer norm = new Normalizer("", Normalizer.COMPOSE_COMPAT, 0);
+        iterateTest(norm, compatTests, 2);
+
+        staticTest(Normalizer.COMPOSE_COMPAT, 0, compatTests, 2);
+    }
+
+    public void TestExplodingBase() {
+        // \u017f - Latin small letter long s
+        // \u0307 - combining dot above
+        // \u1e61 - Latin small letter s with dot above
+        // \u1e9b - Latin small letter long s with dot above
+        String[][] canon = {
+            // Input                Decomposed              Composed
+            { "Tschu\u017f",        "Tschu\u017f",          "Tschu\u017f"       },
+            { "Tschu\u1e9b",        "Tschu\u017f\u0307",    "Tschu\u1e9b"       },
+        };
+        String[][] compat = {
+            // Input                Decomposed              Composed
+            { "\u017f",        "s",              "s"           },
+            { "\u1e9b",        "s\u0307",        "\u1e61"      },
+        };
+
+        staticTest(Normalizer.DECOMP,           0, canon,  1);
+        staticTest(Normalizer.COMPOSE,          0, canon,  2);
+
+        staticTest(Normalizer.DECOMP_COMPAT,    0, compat, 1);
+        staticTest(Normalizer.COMPOSE_COMPAT,   0, compat, 2);
+
+        Normalizer norm = new Normalizer("", Normalizer.DECOMP_COMPAT);
+        iterateTest(norm, compat, 1);
+        backAndForth(norm, compat);
+
+        norm.setMode(Normalizer.COMPOSE_COMPAT);
+        iterateTest(norm, compat, 2);
+        backAndForth(norm, compat);
+    }
+
+
+    /** The Tibetan vowel sign AA, 0f71, was messed up prior to Unicode version 2.1.9
+     & Once 2.1.9 or 3.0 is released, uncomment this test
+    public void TestTibetan() {
+        String[][] decomp = {
+            { "\u0f77", "\u0f77", "\u0fb2\u0f71\u0f80" }
+        };
+        String[][] compose = {
+            { "\u0fb2\u0f71\u0f80", "\u0fb2\u0f71\u0f80", "\u0fb2\u0f71\u0f80" }
+        };
+
+        staticTest(Normalizer.DECOMP,           0, decomp, 1);
+        staticTest(Normalizer.DECOMP_COMPAT,    0, decomp, 2);
+        staticTest(Normalizer.COMPOSE,          0, compose, 1);
+        staticTest(Normalizer.COMPOSE_COMPAT,   0, compose, 2);
+    }
+    */
+
+
+    //------------------------------------------------------------------------
+    // Internal utilities
+    //
+
+    private void backAndForth(Normalizer iter, String input)
+    {
+        iter.setText(input);
+
+        // Run through the iterator forwards and stick it into a StringBuffer
+        StringBuffer forward =  new StringBuffer();
+        for (char ch = iter.first(); ch != iter.DONE; ch = iter.next()) {
+            forward.append(ch);
+        }
+
+        // Now do it backwards
+        StringBuffer reverse = new StringBuffer();
+        for (char ch = iter.last(); ch != iter.DONE; ch = iter.previous()) {
+            reverse.insert(0, ch);
+        }
+
+        if (!forward.toString().equals(reverse.toString())) {
+            errln("Forward/reverse mismatch for input " + hex(input)
+                  + ", forward: " + hex(forward) + ", backward: " + hex(reverse));
+        }
+    }
+
+    private void backAndForth(Normalizer iter, String[][] tests)
+    {
+        for (int i = 0; i < tests.length; i++)
+        {
+            iter.setText(tests[i][0]);
+
+            // Run through the iterator forwards and stick it into a StringBuffer
+            StringBuffer forward =  new StringBuffer();
+            for (char ch = iter.first(); ch != iter.DONE; ch = iter.next()) {
+                forward.append(ch);
+            }
+
+            // Now do it backwards
+            StringBuffer reverse = new StringBuffer();
+            for (char ch = iter.last(); ch != iter.DONE; ch = iter.previous()) {
+                reverse.insert(0, ch);
+            }
+
+            if (!forward.toString().equals(reverse.toString())) {
+                errln("Forward/reverse mismatch for input " + hex(tests[i][0])
+                    + ", forward: " + hex(forward) + ", backward: " + hex(reverse));
+            }
+        }
+    }
+
+    private void staticTest(Normalizer.Mode mode, int options, String[][] tests, int outCol)
+    {
+        for (int i = 0; i < tests.length; i++)
+        {
+            String input = tests[i][0];
+            String expect = tests[i][outCol];
+
+            logln("Normalizing '" + input + "' (" + hex(input) + ")" );
+
+            String output = Normalizer.normalize(input, mode, options);
+
+            if (!output.equals(expect)) {
+                errln("ERROR: case " + i
+                    + " expected '" + expect + "' (" + hex(expect) + ")"
+                    + " but got '" + output + "' (" + hex(output) + ")" );
+            }
+        }
+    }
+
+    private void iterateTest(Normalizer iter, String[][] tests, int outCol)
+    {
+        for (int i = 0; i < tests.length; i++)
+        {
+            String input = tests[i][0];
+            String expect = tests[i][outCol];
+
+            logln("Normalizing '" + input + "' (" + hex(input) + ")" );
+
+            iter.setText(input);
+            assertEqual(expect, iter, "ERROR: case " + i + " ");
+        }
+    }
+
+    private void assertEqual(String expected, Normalizer iter, String errPrefix)
+    {
+        int index = 0;
+        for (char ch = iter.first(); ch != iter.DONE; ch = iter.next())
+        {
+            if (index >= expected.length()) {
+                errln(errPrefix + "Unexpected character '" + ch + "' (" + hex(ch) + ")"
+                        + " at index " + index);
+                break;
+            }
+            char want = expected.charAt(index);
+            if (ch != want) {
+                errln(errPrefix + "got '" + ch + "' (" + hex(ch) + ")"
+                        + " but expected '" + want + "' (" + hex(want) + ")"
+                        + " at index " + index);
+            }
+            index++;
+        }
+        if (index < expected.length()) {
+            errln(errPrefix + "Only got " + index + " chars, expected " + expected.length());
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/ibm/test/normalizer/ExhaustiveTest.java b/src/com/ibm/test/normalizer/ExhaustiveTest.java
new file mode 100755
index 0000000..dd9e0ff
--- /dev/null
+++ b/src/com/ibm/test/normalizer/ExhaustiveTest.java
@@ -0,0 +1,146 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/normalizer/Attic/ExhaustiveTest.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.normalizer;
+
+import com.ibm.test.*;
+import com.ibm.text.*;
+
+public class ExhaustiveTest extends TestFmwk
+{
+    private UInfo info;
+	
+    public static void main(String[] args) throws Exception
+    {
+    	UInfo tempInfo = null;
+        String[] tempArgs = new String[args.length];
+        int count = 0;
+
+        // Allow the test to be pointed at a specific version of the Unicode database
+        for (int i = 0; i < args.length; i++)
+        {
+            if (args[i].equals("-data")) {
+                tempInfo = new UInfo(args[++i]);
+            } else {
+                tempArgs[count++] = args[i];
+            }
+        }
+
+        args = new String[count];
+        System.arraycopy(tempArgs, 0, args, 0, count);
+
+
+        if (tempInfo == null) {
+            tempInfo = new UInfo("../src/data/unicode/UnicodeData.txt");
+	    }
+        new ExhaustiveTest(tempInfo).run(args);
+    }
+    
+    public ExhaustiveTest() {
+    	this.info = new UInfo();
+    }
+
+    public ExhaustiveTest(UInfo info) {
+    	this.info = info;
+    }
+
+
+    /**
+     * Run through all of the characters returned by a composed-char iterator
+     * and make sure that:
+     * <ul>
+     * <li>a) They do indeed have decompositions.
+     * <li>b) The decomposition according to the iterator is the same as
+     *          returned by Normalizer.decompose().
+     * <li>c) All characters <em>not</em> returned by the iterator do not
+     *          have decompositions.
+     * </ul>
+     */
+    public void TestComposedCharIter() {
+        doTestComposedChars(false);
+    }
+
+    public void doTestComposedChars(boolean compat) {
+        int options = Normalizer.IGNORE_HANGUL;
+        ComposedCharIter iter = new ComposedCharIter(compat, options);
+
+        char lastChar = 0;
+
+        while (iter.hasNext()) {
+            char ch = iter.next();
+
+            // Test all characters between the last one and this one to make
+            // sure that they don't have decompositions
+            assertNoDecomp(lastChar, ch, compat, options);
+            lastChar = ch;
+
+            // Now make sure that the decompositions for this character
+            // make sense
+            String chString   = new StringBuffer().append(ch).toString();
+            String iterDecomp = iter.decomposition();
+            String normDecomp = Normalizer.decompose(chString, compat, 0);
+
+            if (iterDecomp.equals(chString)) {
+                errln("ERROR: " + hex(ch) + " has identical decomp");
+            }
+            else if (!iterDecomp.equals(normDecomp)) {
+                errln("ERROR: Normalizer decomp for " + hex(ch) + " (" + hex(normDecomp) + ")"
+                    + " != iter decomp (" + hex(iterDecomp) + ")" );
+            }
+        }
+        assertNoDecomp(lastChar, '\uFFFF', compat, options);
+    }
+
+    void assertNoDecomp(char start, char limit, boolean compat, int options)
+    {
+        for (char x = ++start; x < limit; x++) {
+            String xString   = new StringBuffer().append(x).toString();
+            String decomp = Normalizer.decompose(xString, compat, options);
+            if (!decomp.equals(xString)) {
+                errln("ERROR: " + hex(x) + " has decomposition (" + hex(decomp) + ")"
+                    + " but was not returned by iterator");
+            }
+        }
+    }
+
+
+    public void TestRoundTrip() {
+        int options = Normalizer.IGNORE_HANGUL;
+        boolean compat = false;
+
+        ComposedCharIter iter = new ComposedCharIter(false, options);
+        while (iter.hasNext()) {
+            char ch = iter.next();
+
+            String chStr = new StringBuffer().append(ch).toString();
+            String decomp = Normalizer.decompose(chStr, compat, options);
+            String comp = Normalizer.compose(decomp, compat, options);
+
+            short cClass = info.getCanonicalClass(decomp.charAt(0));
+
+            if (info.isExcludedComposition(ch)) {
+                logln("Skipped excluded char " + hex(ch) + " (" + info.getName(ch,true) + ")" );
+                continue;
+            }
+
+            // Avoid disparaged characters
+            if (info.getDecomposition(ch).length() == 4) continue;
+
+            if (!comp.equals(chStr)) {
+                errln("ERROR: Round trip invalid: " + hex(chStr) + " --> " + hex(decomp)
+                    + " --> " + hex(comp));
+
+                errln("  char decomp is '" + info.getDecomposition(ch) + "'");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/ibm/test/rbbi/BreakIteratorRules_en_US_TEST.java b/src/com/ibm/test/rbbi/BreakIteratorRules_en_US_TEST.java
new file mode 100755
index 0000000..cae8e3f
--- /dev/null
+++ b/src/com/ibm/test/rbbi/BreakIteratorRules_en_US_TEST.java
@@ -0,0 +1,113 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/rbbi/Attic/BreakIteratorRules_en_US_TEST.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.text.resources;
+
+import java.util.ListResourceBundle;
+import java.net.URL;
+
+/**
+ * This resource bundle is included for testing and demonstration purposes only.
+ * It applies the dictionary-based algorithm to English text that has had all the
+ * spaces removed.  Once we have good test cases for Thai, we will replace this
+ * with good resource data (and a good dictionary file) for Thai
+ */
+public class BreakIteratorRules_en_US_TEST extends ListResourceBundle {
+    public Object[][] getContents() {
+        URL url = getClass().getResource("english.dict");
+
+        // if dictionary wasn't found, then this resource bundle doesn't have
+        // much to contribute...
+        if (url == null) {
+            return new Object[0][0];
+        }
+        return new Object[][]  {
+        	// names of classes to instantiate for the different kinds of break
+        	// iterator.  Notice we're now using DictionaryBasedBreakIterator
+        	// for word and line breaking.
+        	{ "BreakIteratorClasses",
+            	new String[] { "RuleBasedBreakIterator",           // character-break iterator class
+                        	"DictionaryBasedBreakIterator",     // word-break iterator class
+                        	"DictionaryBasedBreakIterator",     // line-break iterator class
+                        	"RuleBasedBreakIterator" }          // sentence-break iterator class
+        	},
+	        
+        	// These are the same word-breaking rules as are specified in the default
+        	// resource, except that the Latin letters, apostrophe, and hyphen are
+        	// specified as dictionary characters
+        	{ "WordBreakRules",
+            	"$ignore=[[:Mn:][:Me:][:Cf:]];"
+            	+ "dictionary=[a-zA-z\\'\\-];"
+            	+ "kanji=[\u3005\u4e00-\u9fa5\uf900-\ufa2d];"
+            	+ "kata=[\u30a1-\u30fa];"
+            	+ "hira=[\u3041-\u3094];"
+            	+ "cjk-diacrit=[\u3099-\u309c];"
+            	+ "let=[[:L:]^[{kanji}{kata}{hira}{cjk-diacrit}{dictionary}]];"
+            	+ "dgt=[[:N:]];"
+            	+ "mid-word=[[:Pd:]\u00ad\u2027\\\"\\\'\\.];"
+            	+ "mid-num=[\\\"\\\'\\,\u066b\\.];"
+            	+ "pre-num=[[:Sc:]\\#\\.^\u00a2];"
+            	+ "post-num=[\\%\\&\u00a2\u066a\u2030\u2031];"
+            	+ "ls=[\n\u000c\u2028\u2029];"
+            	+ "ws=[[:Zs:]\t];"
+            	+ "word=({let}{let}*({mid-word}{let}{let}*)*|[a-zA-Z][a-z\\'\\-]*);"
+            	+ "number=({dgt}{dgt}*({mid-num}{dgt}{dgt}*)*);"
+            	+ ".;"
+            	+ "{{word}}({number}{word})*{{number}{{post-num}}};"
+            	+ "{pre-num}({number}{word})*{{number}{{post-num}}};"
+            	+ "{ws}*{\r}{{ls}};"
+            	+ "[{kata}{cjk-diacrit}]*;"
+            	+ "[{hira}{cjk-diacrit}]*;"
+            	+ "{kanji}*;" },
+	        
+        	// These are the same line-breaking rules as are specified in the default
+        	// resource, except that the Latin letters, apostrophe, and hyphen are
+        	// specified as dictionary characters
+        	{ "LineBreakRules",
+            	"<ignore>=[:Mn::Me::Cf:];"
+            	+ "<dictionary>=[a-zA-z\\'\\-];"
+            	+ "<break>=[\u0003\t\n\f\u2028\u2029];"
+            	+ "<nbsp>=[\u00a0\u2007\u2011\ufeff];"
+            	+ "<space>=[:Zs::Cc:^[<nbsp><break>\r]];"
+            	+ "<dash>=[:Pd:\u00ad^<nbsp>];"
+            	+ "<pre-word>=[:Sc::Ps:^\u00a2];"
+            	+ "<post-word>=[:Pe:\\!\\%\\.\\,\\:\\;\\?\u00a2\u00b0\u066a\u2030-\u2034\u2103"
+                    	+ "\u2105\u2109\u3001\u3002\u3005\u3041\u3043\u3045\u3047\u3049\u3063"
+                    	+ "\u3083\u3085\u3087\u308e\u3099-\u309e\u30a1\u30a3\u30a5\u30a7\u30a9"
+                    	+ "\u30c3\u30e3\u30e5\u30e7\u30ee\u30f5\u30f6\u30fc-\u30fe\uff01\uff0e"
+                    	+ "\uff1f];"
+            	+ "<kanji>=[\u4e00-\u9fa5\uf900-\ufa2d\u3041-\u3094\u30a1-\u30fa^[<post-word><ignore>]];"
+            	+ "<digit>=[:Nd::No:];"
+            	+ "<mid-num>=[\\.\\,];"
+            	+ "<char>=[^[<break><space><dash><kanji><nbsp><ignore><dictionary>\r]];"
+            	+ "<number>=([<pre-word><dash>]*<digit><digit>*(<mid-num><digit><digit>*)*);"
+            	+ "<word-core>=(<char>*|<kanji>|<number>|[a-zA-Z][a-z\\'\\-]*);"
+            	+ "<word-suffix>=((<dash><dash>*|<post-word>*)<space>*);"
+            	+ "<word>=(<pre-word>*<word-core><word-suffix>);"
+            	+ "<word>(<nbsp><nbsp>*<word>)*{\r}{<break>};" },
+	            
+        	// these two resources specify the pathnames of the dictionary files to
+        	// use for word breaking and line breaking.  Both currently refer to 
+        	// a file called english.dict placed in com\ibm\text\resources
+        	// somewhere in the class path.  It's important to note that
+        	// english.dict was created for testing purposes only, and doesn't
+        	// come anywhere close to being an exhaustive dictionary of English
+        	// words (basically, it contains all the words in the Declaration of
+        	// Independence, and the Revised Standard Version of the book of Genesis,
+        	// plus a few other words thrown in to show more interesting cases).
+	//        { "WordBreakDictionary", "com\\ibm\\text\\resources\\english.dict" },
+	//       { "LineBreakDictionary", "com\\ibm\\text\\resources\\english.dict" }
+        	{ "WordBreakDictionary", url },
+        	{ "LineBreakDictionary", url }
+    	};
+    }
+}
diff --git a/src/com/ibm/test/rbbi/BreakIteratorTest.java b/src/com/ibm/test/rbbi/BreakIteratorTest.java
new file mode 100755
index 0000000..fc05934
--- /dev/null
+++ b/src/com/ibm/test/rbbi/BreakIteratorTest.java
@@ -0,0 +1,1308 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/rbbi/Attic/BreakIteratorTest.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.4 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.rbbi;
+
+import com.ibm.test.*;
+import com.ibm.text.BreakIterator;
+import java.text.CharacterIterator;
+import com.ibm.text.StringCharacterIterator;
+import java.util.Locale;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.*;
+
+public class BreakIteratorTest extends TestFmwk
+{
+    private BreakIterator characterBreak;
+    private BreakIterator wordBreak;
+    private BreakIterator lineBreak;
+    private BreakIterator sentenceBreak;
+
+    public static void main(String[] args) throws Exception {
+        new BreakIteratorTest().run(args);
+    }
+
+    public BreakIteratorTest()
+    {
+        characterBreak = BreakIterator.getCharacterInstance();
+        wordBreak = BreakIterator.getWordInstance();
+        lineBreak = BreakIterator.getLineInstance();
+System.out.println("Creating sentence iterator...");
+        sentenceBreak = BreakIterator.getSentenceInstance();
+System.out.println("Finished creating sentence iterator...");
+    }
+
+    //=========================================================================
+    // general test subroutines
+    //=========================================================================
+
+    private void generalIteratorTest(BreakIterator bi, Vector expectedResult) {
+        StringBuffer buffer = new StringBuffer();
+        String text;
+        for (int i = 0; i < expectedResult.size(); i++) {
+            text = (String)expectedResult.elementAt(i);
+            buffer.append(text);
+        }
+        text = buffer.toString();
+
+        bi.setText(text);
+
+        Vector nextResults = _testFirstAndNext(bi, text);
+        Vector previousResults = _testLastAndPrevious(bi, text);
+
+        logln("comparing forward and backward...");
+        int errs = getErrorCount();
+        compareFragmentLists("forward iteration", "backward iteration", nextResults,
+                        previousResults);
+        if (getErrorCount() == errs) {
+            logln("comparing expected and actual...");
+            compareFragmentLists("expected result", "actual result", expectedResult,
+                            nextResults);
+        }
+
+        int[] boundaries = new int[expectedResult.size() + 3];
+        boundaries[0] = BreakIterator.DONE;
+        boundaries[1] = 0;
+        for (int i = 0; i < expectedResult.size(); i++)
+            boundaries[i + 2] = boundaries[i + 1] + ((String)expectedResult.elementAt(i)).
+                            length();
+        boundaries[boundaries.length - 1] = BreakIterator.DONE;
+
+        _testFollowing(bi, text, boundaries);
+        _testPreceding(bi, text, boundaries);
+        _testIsBoundary(bi, text, boundaries);
+
+        doMultipleSelectionTest(bi, text);
+    }
+
+    private Vector _testFirstAndNext(BreakIterator bi, String text) {
+        int p = bi.first();
+        int lastP = p;
+        Vector result = new Vector();
+
+        if (p != 0)
+            errln("first() returned " + p + " instead of 0");
+        while (p != BreakIterator.DONE) {
+            p = bi.next();
+            if (p != BreakIterator.DONE) {
+                if (p <= lastP)
+                    errln("next() failed to move forward: next() on position "
+                                    + lastP + " yielded " + p);
+
+                result.addElement(text.substring(lastP, p));
+            }
+            else {
+                if (lastP != text.length())
+                    errln("next() returned DONE prematurely: offset was "
+                                    + lastP + " instead of " + text.length());
+            }
+            lastP = p;
+        }
+        return result;
+    }
+
+    private Vector _testLastAndPrevious(BreakIterator bi, String text) {
+        int p = bi.last();
+        int lastP = p;
+        Vector result = new Vector();
+
+        if (p != text.length())
+            errln("last() returned " + p + " instead of " + text.length());
+        while (p != BreakIterator.DONE) {
+            p = bi.previous();
+            if (p != BreakIterator.DONE) {
+                if (p >= lastP)
+                    errln("previous() failed to move backward: previous() on position "
+                                    + lastP + " yielded " + p);
+
+                result.insertElementAt(text.substring(p, lastP), 0);
+            }
+            else {
+                if (lastP != 0)
+                    errln("previous() returned DONE prematurely: offset was "
+                                    + lastP + " instead of 0");
+            }
+            lastP = p;
+        }
+        return result;
+    }
+
+    private void compareFragmentLists(String f1Name, String f2Name, Vector f1, Vector f2) {
+        int p1 = 0;
+        int p2 = 0;
+        String s1;
+        String s2;
+        int t1 = 0;
+        int t2 = 0;
+
+        while (p1 < f1.size() && p2 < f2.size()) {
+            s1 = (String)f1.elementAt(p1);
+            s2 = (String)f2.elementAt(p2);
+            t1 += s1.length();
+            t2 += s2.length();
+
+            if (s1.equals(s2)) {
+                debugLogln("   >" + s1 + "<");
+                ++p1;
+                ++p2;
+            }
+            else {
+                int tempT1 = t1;
+                int tempT2 = t2;
+                int tempP1 = p1;
+                int tempP2 = p2;
+
+                while (tempT1 != tempT2 && tempP1 < f1.size() && tempP2 < f2.size()) {
+                    while (tempT1 < tempT2 && tempP1 < f1.size()) {
+                        tempT1 += ((String)f1.elementAt(tempP1)).length();
+                        ++tempP1;
+                    }
+                    while (tempT2 < tempT1 && tempP2 < f2.size()) {
+                        tempT2 += ((String)f2.elementAt(tempP2)).length();
+                        ++tempP2;
+                    }
+                }
+                logln("*** " + f1Name + " has:");
+                while (p1 <= tempP1 && p1 < f1.size()) {
+                    s1 = (String)f1.elementAt(p1);
+                    t1 += s1.length();
+                    debugLogln(" *** >" + s1 + "<");
+                    ++p1;
+                }
+                logln("***** " + f2Name + " has:");
+                while (p2 <= tempP2 && p2 < f2.size()) {
+                    s2 = (String)f2.elementAt(p2);
+                    t2 += s2.length();
+                    debugLogln(" ***** >" + s2 + "<");
+                    ++p2;
+                }
+                errln("Discrepancy between " + f1Name + " and " + f2Name);
+            }
+        }
+    }
+
+    private void _testFollowing(BreakIterator bi, String text, int[] boundaries) {
+        logln("testFollowing():");
+        int p = 2;
+        for (int i = 0; i <= text.length(); i++) {
+            if (i == boundaries[p])
+                ++p;
+
+            int b = bi.following(i);
+            logln("bi.following(" + i + ") -> " + b);
+            if (b != boundaries[p])
+                errln("Wrong result from following() for " + i + ": expected " + boundaries[p]
+                                + ", got " + b);
+        }
+    }
+
+    private void _testPreceding(BreakIterator bi, String text, int[] boundaries) {
+        logln("testPreceding():");
+        int p = 0;
+        for (int i = 0; i <= text.length(); i++) {
+            int b = bi.preceding(i);
+            logln("bi.preceding(" + i + ") -> " + b);
+            if (b != boundaries[p])
+                errln("Wrong result from preceding() for " + i + ": expected " + boundaries[p]
+                                + ", got " + b);
+
+            if (i == boundaries[p + 1])
+                ++p;
+        }
+    }
+
+    private void _testIsBoundary(BreakIterator bi, String text, int[] boundaries) {
+        logln("testIsBoundary():");
+        int p = 1;
+        boolean isB;
+        for (int i = 0; i <= text.length(); i++) {
+            isB = bi.isBoundary(i);
+            logln("bi.isBoundary(" + i + ") -> " + isB);
+
+            if (i == boundaries[p]) {
+                if (!isB)
+                    errln("Wrong result from isBoundary() for " + i + ": expected true, got false");
+                ++p;
+            }
+            else {
+                if (isB)
+                    errln("Wrong result from isBoundary() for " + i + ": expected false, got true");
+            }
+        }
+    }
+
+    private void doMultipleSelectionTest(BreakIterator iterator, String testText)
+    {
+        logln("Multiple selection test...");
+        BreakIterator testIterator = (BreakIterator)iterator.clone();
+        int offset = iterator.first();
+        int testOffset;
+        int count = 0;
+
+        do {
+            testOffset = testIterator.first();
+            testOffset = testIterator.next(count);
+            logln("next(" + count + ") -> " + testOffset);
+            if (offset != testOffset)
+                errln("next(n) and next() not returning consistent results: for step " + count + ", next(n) returned " + testOffset + " and next() had " + offset);
+
+            if (offset != BreakIterator.DONE) {
+                count++;
+                offset = iterator.next();
+            }
+        } while (offset != BreakIterator.DONE);
+
+        // now do it backwards...
+        offset = iterator.last();
+        count = 0;
+
+        do {
+            testOffset = testIterator.last();
+            testOffset = testIterator.next(count);
+            logln("next(" + count + ") -> " + testOffset);
+            if (offset != testOffset)
+                errln("next(n) and next() not returning consistent results: for step " + count + ", next(n) returned " + testOffset + " and next() had " + offset);
+
+            if (offset != BreakIterator.DONE) {
+                count--;
+                offset = iterator.previous();
+            }
+        } while (offset != BreakIterator.DONE);
+    }
+
+    private void doBreakInvariantTest(BreakIterator tb, String testChars)
+    {
+        StringBuffer work = new StringBuffer("aaa");
+//        int errorCount = 0;
+
+        // a break should always occur after CR (unless followed by LF), LF, PS, and LS,
+        // unless they're followed by a non-spacing mark or a format character
+        String breaks = "\r\n\u2029\u2028";
+
+        for (int i = 0; i < breaks.length(); i++) {
+            work.setCharAt(1, breaks.charAt(i));
+            for (int j = 0; j < testChars.length(); j++) {
+                work.setCharAt(0, testChars.charAt(j));
+                for (int k = 0; k < testChars.length(); k++) {
+                    char c = testChars.charAt(k);
+
+                    // if a cr is followed by lf, ps, ls or etx, don't do the check (that's
+                    // not supposed to work)
+                    if (work.charAt(1) == '\r' && (c == '\n' || c == '\u2029'
+                            || c == '\u2028' || c == '\u0003'))
+                        continue;
+
+                    work.setCharAt(2, c);
+                    tb.setText(work.toString());
+                    boolean seen2 = false;
+                    for (int l = tb.first(); l != BreakIterator.DONE; l = tb.next()) {
+                        if (l == 2)
+                            seen2 = true;
+                    }
+                    if (!seen2) {
+                        errln("No break between U+" + Integer.toHexString((int)(work.charAt(1)))
+                                    + " and U+" + Integer.toHexString((int)(work.charAt(2))));
+                    }
+                }
+            }
+        }
+    }
+
+    private void doOtherInvariantTest(BreakIterator tb, String testChars)
+    {
+        StringBuffer work = new StringBuffer("a\r\na");
+        int errorCount = 0;
+
+        // a break should never occur between CR and LF
+        for (int i = 0; i < testChars.length(); i++) {
+            work.setCharAt(0, testChars.charAt(i));
+            for (int j = 0; j < testChars.length(); j++) {
+                work.setCharAt(3, testChars.charAt(j));
+                tb.setText(work.toString());
+                for (int k = tb.first(); k != BreakIterator.DONE; k = tb.next())
+                    if (k == 2) {
+                        errln("Break between CR and LF in string U+" + Integer.toHexString(
+                                (int)(work.charAt(0))) + ", U+d U+a U+" + Integer.toHexString(
+                                (int)(work.charAt(3))));
+                        errorCount++;
+                        if (errorCount >= 75)
+                            return;
+                    }
+            }
+        }
+
+        // a break should never occur before a non-spacing mark, unless it's preceded
+        // by a line terminator
+        work.setLength(0);
+        work.append("aaaa");
+        for (int i = 0; i < testChars.length(); i++) {
+            char c = testChars.charAt(i);
+            if (c == '\n' || c == '\r' || c == '\u2029' || c == '\u2028' || c == '\u0003')
+                continue;
+            work.setCharAt(1, c);
+            for (int j = 0; j < testChars.length(); j++) {
+                c = testChars.charAt(j);
+                if (Character.getType(c) != Character.NON_SPACING_MARK && Character.getType(c)
+                        != Character.ENCLOSING_MARK)
+                    continue;
+                work.setCharAt(2, c);
+                tb.setText(work.toString());
+                for (int k = tb.first(); k != BreakIterator.DONE; k = tb.next())
+                    if (k == 2) {
+                        errln("Break between U+" + Integer.toHexString((int)(work.charAt(1)))
+                                + " and U+" + Integer.toHexString((int)(work.charAt(2))));
+                        errorCount++;
+                        if (errorCount >= 75)
+                            return;
+                    }
+            }
+        }
+    }
+
+    public void debugLogln(String s) {
+        final String zeros = "0000";
+        String temp;
+        StringBuffer out = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c >= ' ' && c < '\u007f')
+                out.append(c);
+            else {
+                out.append("\\u");
+                temp = Integer.toHexString((int)c);
+                out.append(zeros.substring(0, 4 - temp.length()));
+                out.append(temp);
+            }
+        }
+        logln(out.toString());
+    }
+
+    //=========================================================================
+    // tests
+    //=========================================================================
+
+    public void TestWordBreak() {
+        Vector wordSelectionData = new Vector();
+
+        wordSelectionData.addElement("12,34");
+
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\u00A2"); //cent sign
+        wordSelectionData.addElement("\u00A3"); //pound sign
+        wordSelectionData.addElement("\u00A4"); //currency sign
+        wordSelectionData.addElement("\u00A5"); //yen sign
+        wordSelectionData.addElement("alpha-beta-gamma");
+        wordSelectionData.addElement(".");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("Badges");
+        wordSelectionData.addElement("?");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("BADGES");
+        wordSelectionData.addElement("!");
+        wordSelectionData.addElement("?");
+        wordSelectionData.addElement("!");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("We");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("don't");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("need");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("no");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("STINKING");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("BADGES");
+        wordSelectionData.addElement("!");
+        wordSelectionData.addElement("!");
+        wordSelectionData.addElement("!");
+
+        wordSelectionData.addElement("012.566,5");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("123.3434,900");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("1000,233,456.000");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("1,23.322%");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("123.1222");
+
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\u0024123,000.20");
+
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("179.01\u0025");
+
+        wordSelectionData.addElement("Hello");
+        wordSelectionData.addElement(",");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("how");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("are");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("you");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("X");
+        wordSelectionData.addElement(" ");
+
+        wordSelectionData.addElement("Now");
+        wordSelectionData.addElement("\r");
+        wordSelectionData.addElement("is");
+        wordSelectionData.addElement("\n");
+        wordSelectionData.addElement("the");
+        wordSelectionData.addElement("\r\n");
+        wordSelectionData.addElement("time");
+        wordSelectionData.addElement("\n");
+        wordSelectionData.addElement("\r");
+        wordSelectionData.addElement("for");
+        wordSelectionData.addElement("\r");
+        wordSelectionData.addElement("\r");
+        wordSelectionData.addElement("all");
+        wordSelectionData.addElement(" ");
+
+        generalIteratorTest(wordBreak, wordSelectionData);
+    }
+
+    /**
+     * @bug 4097779
+     */
+    public void TestBug4097779() {
+        Vector wordSelectionData = new Vector();
+
+        wordSelectionData.addElement("aa\u0300a");
+        wordSelectionData.addElement(" ");
+
+        generalIteratorTest(wordBreak, wordSelectionData);
+    }
+
+    /**
+     * @bug 4098467
+     */
+    public void TestBug4098467Words() {
+        Vector wordSelectionData = new Vector();
+
+        // What follows is a string of Korean characters (I found it in the Yellow Pages
+        // ad for the Korean Presbyterian Church of San Francisco, and I hope I transcribed
+        // it correctly), first as precomposed syllables, and then as conjoining jamo.
+        // Both sequences should be semantically identical and break the same way.
+        // precomposed syllables...
+        wordSelectionData.addElement("\uc0c1\ud56d");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\ud55c\uc778");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\uc5f0\ud569");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\uc7a5\ub85c\uad50\ud68c");
+        wordSelectionData.addElement(" ");
+        // conjoining jamo...
+        wordSelectionData.addElement("\u1109\u1161\u11bc\u1112\u1161\u11bc");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\u1112\u1161\u11ab\u110b\u1175\u11ab");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\u110b\u1167\u11ab\u1112\u1161\u11b8");
+        wordSelectionData.addElement(" ");
+        wordSelectionData.addElement("\u110c\u1161\u11bc\u1105\u1169\u1100\u116d\u1112\u116c");
+        wordSelectionData.addElement(" ");
+
+        generalIteratorTest(wordBreak, wordSelectionData);
+    }
+
+    /**
+     * @bug 4117554
+     */
+    public void TestBug4117554Words() {
+        Vector wordSelectionData = new Vector();
+
+        // this is a test for bug #4117554: the ideographic iteration mark (U+3005) should
+        // count as a Kanji character for the purposes of word breaking
+        wordSelectionData.addElement("abc");
+        wordSelectionData.addElement("\u4e01\u4e02\u3005\u4e03\u4e03");
+        wordSelectionData.addElement("abc");
+
+        generalIteratorTest(wordBreak, wordSelectionData);
+    }
+
+    public void TestSentenceBreak() {
+        Vector sentenceSelectionData = new Vector();
+
+        sentenceSelectionData.addElement("This is a simple sample sentence. ");
+        sentenceSelectionData.addElement("(This is it.) ");
+        sentenceSelectionData.addElement("This is a simple sample sentence. ");
+        sentenceSelectionData.addElement("\"This isn\'t it.\" ");
+        sentenceSelectionData.addElement("Hi! ");
+        sentenceSelectionData.addElement("This is a simple sample sentence. ");
+        sentenceSelectionData.addElement("It does not have to make any sense as you can see. ");
+        sentenceSelectionData.addElement("Nel mezzo del cammin di nostra vita, mi ritrovai in una selva oscura. ");
+        sentenceSelectionData.addElement("Che la dritta via aveo smarrita. ");
+        sentenceSelectionData.addElement("He said, that I said, that you said!! ");
+
+        sentenceSelectionData.addElement("Don't rock the boat.\u2029");
+
+        sentenceSelectionData.addElement("Because I am the daddy, that is why. ");
+        sentenceSelectionData.addElement("Not on my time (el timo.)! ");
+
+        sentenceSelectionData.addElement("So what!!\u2029");
+
+        sentenceSelectionData.addElement("\"But now,\" he said, \"I know!\" ");
+        sentenceSelectionData.addElement("Harris thumbed down several, including \"Away We Go\" (which became the huge success Oklahoma!). ");
+        sentenceSelectionData.addElement("One species, B. anthracis, is highly virulent.\n");
+        sentenceSelectionData.addElement("Wolf said about Sounder:\"Beautifully thought-out and directed.\" ");
+        sentenceSelectionData.addElement("Have you ever said, \"This is where \tI shall live\"? ");
+        sentenceSelectionData.addElement("He answered, \"You may not!\" ");
+        sentenceSelectionData.addElement("Another popular saying is: \"How do you do?\". ");
+        sentenceSelectionData.addElement("Yet another popular saying is: \'I\'m fine thanks.\' ");
+        sentenceSelectionData.addElement("What is the proper use of the abbreviation pp.? ");
+        sentenceSelectionData.addElement("Yes, I am definatelly 12\" tall!!");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4113835
+     */
+    public void TestBug4113835() {
+        Vector sentenceSelectionData = new Vector();
+
+        // test for bug #4113835: \n and \r count as spaces, not as paragraph breaks
+        sentenceSelectionData.addElement("Now\ris\nthe\r\ntime\n\rfor\r\rall\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4111338
+     */
+    public void TestBug4111338() {
+        Vector sentenceSelectionData = new Vector();
+
+        // test for bug #4111338: Don't break sentences at the boundary between CJK
+        // and other letters
+        sentenceSelectionData.addElement("\u5487\u67ff\ue591\u5017\u61b3\u60a1\u9510\u8165:\"JAVA\u821c"
+                + "\u8165\u7fc8\u51ce\u306d,\u2494\u56d8\u4ec0\u60b1\u8560\u51ba"
+                + "\u611d\u57b6\u2510\u5d46\".\u2029");
+        sentenceSelectionData.addElement("\u5487\u67ff\ue591\u5017\u61b3\u60a1\u9510\u8165\u9de8"
+                + "\u97e4JAVA\u821c\u8165\u7fc8\u51ce\u306d\ue30b\u2494\u56d8\u4ec0"
+                + "\u60b1\u8560\u51ba\u611d\u57b6\u2510\u5d46\u97e5\u7751\u2029");
+        sentenceSelectionData.addElement("\u5487\u67ff\ue591\u5017\u61b3\u60a1\u9510\u8165\u9de8\u97e4"
+                + "\u6470\u8790JAVA\u821c\u8165\u7fc8\u51ce\u306d\ue30b\u2494\u56d8"
+                + "\u4ec0\u60b1\u8560\u51ba\u611d\u57b6\u2510\u5d46\u97e5\u7751\u2029");
+        sentenceSelectionData.addElement("He said, \"I can go there.\"\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4117554
+     */
+    public void TestBug4117554Sentences() {
+        Vector sentenceSelectionData = new Vector();
+
+        // Treat fullwidth variants of .!? the same as their
+        // normal counterparts
+        sentenceSelectionData.addElement("I know I'm right\uff0e ");
+        sentenceSelectionData.addElement("Right\uff1f ");
+        sentenceSelectionData.addElement("Right\uff01 ");
+
+        // Don't break sentences at boundary between CJK and digits
+        sentenceSelectionData.addElement("\u5487\u67ff\ue591\u5017\u61b3\u60a1\u9510\u8165\u9de8"
+                + "\u97e48888\u821c\u8165\u7fc8\u51ce\u306d\ue30b\u2494\u56d8\u4ec0"
+                + "\u60b1\u8560\u51ba\u611d\u57b6\u2510\u5d46\u97e5\u7751\u2029");
+
+        // Break sentence between a sentence terminator and
+        // opening punctuation
+        sentenceSelectionData.addElement("no?");
+        sentenceSelectionData.addElement("(yes)");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4158381
+     */
+    public void TestBug4158381() {
+        Vector sentenceSelectionData = new Vector();
+
+        // Don't break sentence after period if it isn't followed by a space
+        sentenceSelectionData.addElement("Test <code>Flags.Flag</code> class.  ");
+        sentenceSelectionData.addElement("Another test.\u2029");
+
+        // No breaks when there are no terminators around
+        sentenceSelectionData.addElement("<P>Provides a set of "
+                + "&quot;lightweight&quot; (all-java<FONT SIZE=\"-2\"><SUP>TM"
+                + "</SUP></FONT> language) components that, "
+                + "to the maximum degree possible, work the same on all platforms.  ");
+        sentenceSelectionData.addElement("Another test.\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4143071
+     */
+    public void TestBug4143071() {
+        Vector sentenceSelectionData = new Vector();
+
+        // Make sure sentences that end with digits work right
+        sentenceSelectionData.addElement("Today is the 27th of May, 1998.  ");
+        sentenceSelectionData.addElement("Tomorrow will be 28 May 1998.  ");
+        sentenceSelectionData.addElement("The day after will be the 30th.\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4152416
+     */
+    public void TestBug4152416() {
+        Vector sentenceSelectionData = new Vector();
+
+        // Make sure sentences ending with a capital letter are treated correctly
+        sentenceSelectionData.addElement("The type of all primitive "
+                + "<code>boolean</code> values accessed in the target VM.  ");
+        sentenceSelectionData.addElement("Calls to xxx will return an "
+                + "implementor of this interface.\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    /**
+     * @bug 4152117
+     */
+    public void TestBug4152117() {
+        Vector sentenceSelectionData = new Vector();
+
+        // Make sure sentence breaking is handling punctuation correctly
+        // [COULD NOT REPRODUCE THIS BUG, BUT TEST IS HERE TO MAKE SURE
+        // IT DOESN'T CROP UP]
+        sentenceSelectionData.addElement("Constructs a randomly generated "
+                + "BigInteger, uniformly distributed over the range <tt>0</tt> "
+                + "to <tt>(2<sup>numBits</sup> - 1)</tt>, inclusive.  ");
+        sentenceSelectionData.addElement("The uniformity of the distribution "
+                + "assumes that a fair source of random bits is provided in "
+                + "<tt>rnd</tt>.  ");
+        sentenceSelectionData.addElement("Note that this constructor always "
+                + "constructs a non-negative BigInteger.\u2029");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    public void TestLineBreak() {
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("Multi-");
+        lineSelectionData.addElement("Level ");
+        lineSelectionData.addElement("example ");
+        lineSelectionData.addElement("of ");
+        lineSelectionData.addElement("a ");
+        lineSelectionData.addElement("semi-");
+        lineSelectionData.addElement("idiotic ");
+        lineSelectionData.addElement("non-");
+        lineSelectionData.addElement("sensical ");
+        lineSelectionData.addElement("(non-");
+        lineSelectionData.addElement("important) ");
+        lineSelectionData.addElement("sentence. ");
+
+        lineSelectionData.addElement("Hi  ");
+        lineSelectionData.addElement("Hello ");
+        lineSelectionData.addElement("How\n");
+        lineSelectionData.addElement("are\r");
+        lineSelectionData.addElement("you\u2028");
+        lineSelectionData.addElement("fine.\t");
+        lineSelectionData.addElement("good.  ");
+
+        lineSelectionData.addElement("Now\r");
+        lineSelectionData.addElement("is\n");
+        lineSelectionData.addElement("the\r\n");
+        lineSelectionData.addElement("time\n");
+        lineSelectionData.addElement("\r");
+        lineSelectionData.addElement("for\r");
+        lineSelectionData.addElement("\r");
+        lineSelectionData.addElement("all");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4068133
+     */
+    public void TestBug4068133() {
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("\u96f6");
+        lineSelectionData.addElement("\u4e00\u3002");
+        lineSelectionData.addElement("\u4e8c\u3001");
+        lineSelectionData.addElement("\u4e09\u3002\u3001");
+        lineSelectionData.addElement("\u56db\u3001\u3002\u3001");
+        lineSelectionData.addElement("\u4e94,");
+        lineSelectionData.addElement("\u516d.");
+        lineSelectionData.addElement("\u4e03.\u3001,\u3002");
+        lineSelectionData.addElement("\u516b");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4086052
+     */
+    public void TestBug4086052() {
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("foo\u00a0bar ");
+//        lineSelectionData.addElement("foo\ufeffbar");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4097920
+     */
+    public void TestBug4097920() {
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("dog,");
+        lineSelectionData.addElement("cat,");
+        lineSelectionData.addElement("mouse ");
+        lineSelectionData.addElement("(one)");
+        lineSelectionData.addElement("(two)\n");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4035266
+     */
+    public void TestBug4035266() {
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("The ");
+        lineSelectionData.addElement("balance ");
+        lineSelectionData.addElement("is ");
+        lineSelectionData.addElement("$-23,456.78, ");
+        lineSelectionData.addElement("not ");
+        lineSelectionData.addElement("-$32,456.78!\n");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4098467
+     */
+    public void TestBug4098467Lines() {
+        Vector lineSelectionData = new Vector();
+
+        // What follows is a string of Korean characters (I found it in the Yellow Pages
+        // ad for the Korean Presbyterian Church of San Francisco, and I hope I transcribed
+        // it correctly), first as precomposed syllables, and then as conjoining jamo.
+        // Both sequences should be semantically identical and break the same way.
+        // precomposed syllables...
+        lineSelectionData.addElement("\uc0c1\ud56d ");
+        lineSelectionData.addElement("\ud55c\uc778 ");
+        lineSelectionData.addElement("\uc5f0\ud569 ");
+        lineSelectionData.addElement("\uc7a5\ub85c\uad50\ud68c ");
+        // conjoining jamo...
+        lineSelectionData.addElement("\u1109\u1161\u11bc\u1112\u1161\u11bc ");
+        lineSelectionData.addElement("\u1112\u1161\u11ab\u110b\u1175\u11ab ");
+        lineSelectionData.addElement("\u110b\u1167\u11ab\u1112\u1161\u11b8 ");
+        lineSelectionData.addElement("\u110c\u1161\u11bc\u1105\u1169\u1100\u116d\u1112\u116c");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    public void TestThaiLineBreak() {
+        Vector lineSelectionData = new Vector();
+
+        // \u0e2f-- the Thai paiyannoi character-- isn't a letter.  It's a symbol that
+        // represents elided letters at the end of a long word.  It should be bound to
+        // the end of the word and not treated as an independent punctuation mark.
+        lineSelectionData.addElement("\u0e2a\u0e16\u0e32\u0e19\u0e35\u0e2f");
+        lineSelectionData.addElement("\u0e08\u0e30");
+        lineSelectionData.addElement("\u0e23\u0e30\u0e14\u0e21");
+        lineSelectionData.addElement("\u0e40\u0e08\u0e49\u0e32");
+//        lineSelectionData.addElement("\u0e2b\u0e19\u0e49\u0e32");
+//        lineSelectionData.addElement("\u0e17\u0e35\u0e48");
+// I think the above two lines are the preferred reading of this text, but our current
+// dictionary yields the following:
+lineSelectionData.addElement("\u0e2b\u0e16\u0e49\u0e32\u0e17\u0e35\u0e48");
+        lineSelectionData.addElement("\u0e2d\u0e2d\u0e01");
+        lineSelectionData.addElement("\u0e21\u0e32");
+        lineSelectionData.addElement("\u0e40\u0e23\u0e48\u0e07");
+        lineSelectionData.addElement("\u0e23\u0e30\u0e1a\u0e32\u0e22");
+        lineSelectionData.addElement("\u0e2d\u0e22\u0e48\u0e32\u0e07");
+        lineSelectionData.addElement("\u0e40\u0e15\u0e47\u0e21");
+
+        // the one time where the paiyannoi occurs somewhere other than at the end
+        // of a word is in the Thai abbrevation for "etc.", which both begins and
+        // ends with a paiyannoi
+        lineSelectionData.addElement("\u0e2f\u0e25\u0e2f");
+        lineSelectionData.addElement("\u0e17\u0e35\u0e48");
+        lineSelectionData.addElement("\u0e19\u0e31\u0e49\u0e19");
+
+        generalIteratorTest(BreakIterator.getLineInstance(new Locale("th", "", "")),
+                lineSelectionData);
+    }
+
+    public void TestMixedThaiLineBreak() {
+        Vector lineSelectionData = new Vector();
+
+        // Arabic numerals should always be separated from surrounding Thai text
+/*
+        lineSelectionData.addElement("\u0e04\u0e48\u0e32");
+        lineSelectionData.addElement("\u0e40\u0e07\u0e34\u0e19");
+        lineSelectionData.addElement("\u0e1a\u0e32\u0e17");
+        lineSelectionData.addElement("\u0e41\u0e15\u0e30");
+        lineSelectionData.addElement("\u0e23\u0e30\u0e14\u0e31\u0e1a");
+        lineSelectionData.addElement("39");
+        lineSelectionData.addElement("\u0e1a\u0e32\u0e17 ");
+
+        // words in non-Thai scripts should always be separated from surrounding Thai text
+        lineSelectionData.addElement("\u0e17\u0e14");
+        lineSelectionData.addElement("\u0e2a\u0e2d\u0e1a");
+        lineSelectionData.addElement("Java");
+        lineSelectionData.addElement("\u0e1a\u0e19");
+        lineSelectionData.addElement("\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07");
+        lineSelectionData.addElement("\u0e44\u0e2d\u0e1a\u0e35\u0e40\u0e2d\u0e47\u0e21 ");
+
+        // Thai numerals should always be separated from the text surrounding them
+        lineSelectionData.addElement("\u0e04\u0e48\u0e32");
+        lineSelectionData.addElement("\u0e40\u0e07\u0e34\u0e19");
+        lineSelectionData.addElement("\u0e1a\u0e32\u0e17");
+        lineSelectionData.addElement("\u0e41\u0e15\u0e30");
+        lineSelectionData.addElement("\u0e23\u0e30\u0e14\u0e31\u0e1a");
+        lineSelectionData.addElement("\u0e53\u0e59");
+        lineSelectionData.addElement("\u0e1a\u0e32\u0e17 ");
+
+        // Thai text should interact correctly with punctuation and symbols
+        lineSelectionData.addElement("\u0e44\u0e2d\u0e1a\u0e35\u0e40\u0e2d\u0e47\u0e21");
+//        lineSelectionData.addElement("(\u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28");
+//        lineSelectionData.addElement("\u0e44\u0e17\u0e22)");
+// I think the above lines represent the preferred reading for this text, but our current
+// dictionary file yields the following:
+lineSelectionData.addElement("(\u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28\u0e44\u0e17\u0e22)");
+        lineSelectionData.addElement("\u0e08\u0e33\u0e01\u0e31\u0e14");
+        lineSelectionData.addElement("\u0e40\u0e1b\u0e34\u0e14");
+        lineSelectionData.addElement("\u0e15\u0e31\u0e27\"");
+*/
+        lineSelectionData.addElement("\u0e2e\u0e32\u0e23\u0e4c\u0e14\u0e14\u0e34\u0e2a\u0e01\u0e4c\"");
+        lineSelectionData.addElement("\u0e23\u0e38\u0e48\u0e19");
+        lineSelectionData.addElement("\u0e43\u0e2b\u0e21\u0e48");
+        lineSelectionData.addElement("\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e21\u0e34.");
+        lineSelectionData.addElement("\u0e22.");
+        lineSelectionData.addElement("\u0e19\u0e35\u0e49");
+        lineSelectionData.addElement("\u0e23\u0e32\u0e04\u0e32");
+        lineSelectionData.addElement("$200");
+        lineSelectionData.addElement("\u0e40\u0e17\u0e48\u0e32");
+        lineSelectionData.addElement("\u0e19\u0e31\u0e49\u0e19 ");
+        lineSelectionData.addElement("(\"\u0e2e\u0e32\u0e23\u0e4c\u0e14\u0e14\u0e34\u0e2a\u0e01\u0e4c\").");
+
+        generalIteratorTest(BreakIterator.getLineInstance(new Locale("th", "", "")),
+                lineSelectionData);
+    }
+
+    public void TestMaiyamok() {
+        Vector lineSelectionData = new Vector();
+
+        // the Thai maiyamok character is a shorthand symbol that means "repeat the previous
+        // word".  Instead of appearing as a word unto itself, however, it's kept together
+        // with the word before it
+        lineSelectionData.addElement("\u0e44\u0e1b\u0e46");
+        lineSelectionData.addElement("\u0e21\u0e32\u0e46");
+        lineSelectionData.addElement("\u0e23\u0e30\u0e2b\u0e27\u0e48\u0e32\u0e07");
+        lineSelectionData.addElement("\u0e01\u0e23\u0e38\u0e07\u0e40\u0e17\u0e1e");
+        lineSelectionData.addElement("\u0e41\u0e25\u0e30");
+        lineSelectionData.addElement("\u0e40\u0e03\u0e35\u0e22\u0e07");
+        lineSelectionData.addElement("\u0e43\u0e2b\u0e21\u0e48");
+
+        generalIteratorTest(BreakIterator.getLineInstance(new Locale("th", "", "")),
+                lineSelectionData);
+    }
+
+    /**
+     * @bug 4117554
+     */
+    public void TestBug4117554Lines() {
+        Vector lineSelectionData = new Vector();
+
+        // Fullwidth .!? should be treated as postJwrd
+        lineSelectionData.addElement("\u4e01\uff0e");
+        lineSelectionData.addElement("\u4e02\uff01");
+        lineSelectionData.addElement("\u4e03\uff1f");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    public void TestLettersAndDigits() {
+        // a character sequence such as "X11" or "30F3" or "native2ascii" should
+        // be kept together as a single word
+        Vector lineSelectionData = new Vector();
+
+        lineSelectionData.addElement("X11 ");
+        lineSelectionData.addElement("30F3 ");
+        lineSelectionData.addElement("native2ascii");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    /**
+     * @bug 4217703
+     */
+    public void TestBug4217703() {
+        Vector lineSelectionData = new Vector();
+
+        // There shouldn't be a line break between sentence-ending punctuation
+        // and a closing quote
+        lineSelectionData.addElement("He ");
+        lineSelectionData.addElement("said ");
+        lineSelectionData.addElement("\"Go!\"  ");
+        lineSelectionData.addElement("I ");
+        lineSelectionData.addElement("went.  ");
+
+        lineSelectionData.addElement("Hashtable$Enumeration ");
+        lineSelectionData.addElement("getText().");
+        lineSelectionData.addElement("getIndex()");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    private static final String graveS = "S\u0300";
+    private static final String acuteBelowI = "i\u0317";
+    private static final String acuteE = "e\u0301";
+    private static final String circumflexA = "a\u0302";
+    private static final String tildeE = "e\u0303";
+
+    public void TestCharacterBreak() {
+        Vector characterSelectionData = new Vector();
+
+        characterSelectionData.addElement(graveS);
+        characterSelectionData.addElement(acuteBelowI);
+        characterSelectionData.addElement("m");
+        characterSelectionData.addElement("p");
+        characterSelectionData.addElement("l");
+        characterSelectionData.addElement(acuteE);
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("s");
+        characterSelectionData.addElement(circumflexA);
+        characterSelectionData.addElement("m");
+        characterSelectionData.addElement("p");
+        characterSelectionData.addElement("l");
+        characterSelectionData.addElement(tildeE);
+        characterSelectionData.addElement(".");
+        characterSelectionData.addElement("w");
+        characterSelectionData.addElement(circumflexA);
+        characterSelectionData.addElement("w");
+        characterSelectionData.addElement("a");
+        characterSelectionData.addElement("f");
+        characterSelectionData.addElement("q");
+        characterSelectionData.addElement("\n");
+        characterSelectionData.addElement("\r");
+        characterSelectionData.addElement("\r\n");
+        characterSelectionData.addElement("\n");
+
+        generalIteratorTest(characterBreak, characterSelectionData);
+    }
+
+    /**
+     * @bug 4098467
+     */
+    public void TestBug4098467Characters() {
+        Vector characterSelectionData = new Vector();
+
+        // What follows is a string of Korean characters (I found it in the Yellow Pages
+        // ad for the Korean Presbyterian Church of San Francisco, and I hope I transcribed
+        // it correctly), first as precomposed syllables, and then as conjoining jamo.
+        // Both sequences should be semantically identical and break the same way.
+        // precomposed syllables...
+        characterSelectionData.addElement("\uc0c1");
+        characterSelectionData.addElement("\ud56d");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\ud55c");
+        characterSelectionData.addElement("\uc778");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\uc5f0");
+        characterSelectionData.addElement("\ud569");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\uc7a5");
+        characterSelectionData.addElement("\ub85c");
+        characterSelectionData.addElement("\uad50");
+        characterSelectionData.addElement("\ud68c");
+        characterSelectionData.addElement(" ");
+        // conjoining jamo...
+        characterSelectionData.addElement("\u1109\u1161\u11bc");
+        characterSelectionData.addElement("\u1112\u1161\u11bc");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\u1112\u1161\u11ab");
+        characterSelectionData.addElement("\u110b\u1175\u11ab");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\u110b\u1167\u11ab");
+        characterSelectionData.addElement("\u1112\u1161\u11b8");
+        characterSelectionData.addElement(" ");
+        characterSelectionData.addElement("\u110c\u1161\u11bc");
+        characterSelectionData.addElement("\u1105\u1169");
+        characterSelectionData.addElement("\u1100\u116d");
+        characterSelectionData.addElement("\u1112\u116c");
+
+        generalIteratorTest(characterBreak, characterSelectionData);
+    }
+
+    /*
+     * @bug 4153072
+     */
+    public void TestBug4153072() {
+        BreakIterator iter = BreakIterator.getWordInstance();
+        String str = "...Hello, World!...";
+        int begin = 3;
+        int end = str.length() - 3;
+        boolean gotException = false;
+        boolean dummy;
+
+        iter.setText(new StringCharacterIterator(str, begin, end, begin));
+        for (int index = -1; index < begin + 1; ++index) {
+            try {
+                dummy = iter.isBoundary(index);
+                if (index < begin)
+                    errln("Didn't get exception with offset = " + index +
+                                    " and begin index = " + begin);
+            }
+            catch (IllegalArgumentException e) {
+                if (index >= begin)
+                    errln("Got exception with offset = " + index +
+                                    " and begin index = " + begin);
+            }
+        }
+    }
+
+    public void TestBug4146175Sentences() {
+        Vector sentenceSelectionData = new Vector();
+
+        // break between periods and opening punctuation even when there's no
+        // intervening space
+        sentenceSelectionData.addElement("end.");
+        sentenceSelectionData.addElement("(This is\u2029");
+
+        // treat the fullwidth period as an unambiguous sentence terminator
+        sentenceSelectionData.addElement("\u7d42\u308f\u308a\uff0e");
+        sentenceSelectionData.addElement("\u300c\u3053\u308c\u306f");
+
+        generalIteratorTest(sentenceBreak, sentenceSelectionData);
+    }
+
+    public void TestBug4146175Lines() {
+        Vector lineSelectionData = new Vector();
+
+        // the fullwidth comma should stick to the preceding Japanese character
+        lineSelectionData.addElement("\u7d42\uff0c");
+        lineSelectionData.addElement("\u308f");
+
+        generalIteratorTest(lineBreak, lineSelectionData);
+    }
+
+    public void TestBug4214367() {
+        Vector wordSelectionData = new Vector();
+
+        // the hiragana and katakana iteration marks and the long vowel mark
+        // are not being treated correctly by the word-break iterator
+        wordSelectionData.addElement("\u3042\u3044\u309d\u3042\u309e\u3042\u30fc\u3042");
+        wordSelectionData.addElement("\u30a2\u30a4\u30fd\u30a2\u30fe\u30a2\u30fc\u30a2");
+
+        generalIteratorTest(wordBreak, wordSelectionData);
+    }
+
+    private static final String cannedTestChars
+        = "\u0000\u0001\u0002\u0003\u0004 !\"#$%&()+-01234<=>ABCDE[]^_`abcde{}|\u00a0\u00a2"
+        + "\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00ab\u00ad\u00ae\u00af\u00b0\u00b2\u00b3"
+        + "\u00b4\u00b9\u00bb\u00bc\u00bd\u02b0\u02b1\u02b2\u02b3\u02b4\u0300\u0301\u0302\u0303"
+        + "\u0304\u05d0\u05d1\u05d2\u05d3\u05d4\u0903\u093e\u093f\u0940\u0949\u0f3a\u0f3b\u2000"
+        + "\u2001\u2002\u200c\u200d\u200e\u200f\u2010\u2011\u2012\u2028\u2029\u202a\u203e\u203f"
+        + "\u2040\u20dd\u20de\u20df\u20e0\u2160\u2161\u2162\u2163\u2164";
+
+    public void TestSentenceInvariants()
+    {
+        BreakIterator e = BreakIterator.getSentenceInstance();
+        doOtherInvariantTest(e, cannedTestChars + ".,\u3001\u3002\u3041\u3042\u3043\ufeff");
+    }
+
+    public void TestWordInvariants()
+    {
+        BreakIterator e = BreakIterator.getWordInstance();
+        doBreakInvariantTest(e, cannedTestChars + "\',.\u3041\u3042\u3043\u309b\u309c\u30a1\u30a2"
+            + "\u30a3\u4e00\u4e01\u4e02");
+        doOtherInvariantTest(e, cannedTestChars + "\',.\u3041\u3042\u3043\u309b\u309c\u30a1\u30a2"
+            + "\u30a3\u4e00\u4e01\u4e02");
+    }
+
+    public void TestLineInvariants()
+    {
+        BreakIterator e = BreakIterator.getLineInstance();
+        String testChars = cannedTestChars + ".,;:\u3001\u3002\u3041\u3042\u3043\u3044\u3045"
+            + "\u30a3\u4e00\u4e01\u4e02";
+        doBreakInvariantTest(e, testChars);
+        doOtherInvariantTest(e, testChars);
+
+        int errorCount = 0;
+
+        // in addition to the other invariants, a line-break iterator should make sure that:
+        // it doesn't break around the non-breaking characters
+        String noBreak = "\u00a0\u2007\u2011\ufeff";
+        StringBuffer work = new StringBuffer("aaa");
+        for (int i = 0; i < testChars.length(); i++) {
+            char c = testChars.charAt(i);
+            if (c == '\r' || c == '\n' || c == '\u2029' || c == '\u2028' || c == '\u0003')
+                continue;
+            work.setCharAt(0, c);
+            for (int j = 0; j < noBreak.length(); j++) {
+                work.setCharAt(1, noBreak.charAt(j));
+                for (int k = 0; k < testChars.length(); k++) {
+                    work.setCharAt(2, testChars.charAt(k));
+                    e.setText(work.toString());
+                    for (int l = e.first(); l != BreakIterator.DONE; l = e.next())
+                        if (l == 1 || l == 2) {
+                            errln("Got break between U+" + Integer.toHexString((int)
+                                    (work.charAt(l - 1))) + " and U+" + Integer.toHexString(
+                                    (int)(work.charAt(l))));
+                            errorCount++;
+                            if (errorCount >= 75)
+                                return;
+                        }
+                }
+            }
+        }
+
+        // it does break after dashes (unless they're followed by a digit, a non-spacing mark,
+        // a currency symbol, a space, a format-control character, a regular control character,
+        // a line or paragraph separator, or another dash)
+        String dashes = "-\u00ad\u2010\u2012\u2013\u2014";
+        for (int i = 0; i < testChars.length(); i++) {
+            work.setCharAt(0, testChars.charAt(i));
+            for (int j = 0; j < dashes.length(); j++) {
+                work.setCharAt(1, dashes.charAt(j));
+                for (int k = 0; k < testChars.length(); k++) {
+                    char c = testChars.charAt(k);
+                    if (Character.getType(c) == Character.DECIMAL_DIGIT_NUMBER ||
+                        Character.getType(c) == Character.OTHER_NUMBER ||
+                        Character.getType(c) == Character.NON_SPACING_MARK ||
+                        Character.getType(c) == Character.ENCLOSING_MARK ||
+                        Character.getType(c) == Character.CURRENCY_SYMBOL ||
+                        Character.getType(c) == Character.DASH_PUNCTUATION ||
+                        Character.getType(c) == Character.SPACE_SEPARATOR ||
+                        Character.getType(c) == Character.FORMAT ||
+                        Character.getType(c) == Character.CONTROL ||
+                        c == '\n' || c == '\r' || c == '\u2028' || c == '\u2029' ||
+                        c == '\u0003' || c == '\u2007' || c == '\u2011' ||
+                        c == '\ufeff')
+                        continue;
+                    work.setCharAt(2, c);
+                    e.setText(work.toString());
+                    boolean saw2 = false;
+                    for (int l = e.first(); l != BreakIterator.DONE; l = e.next())
+                        if (l == 2)
+                            saw2 = true;
+                    if (!saw2) {
+                        errln("Didn't get break between U+" + Integer.toHexString((int)
+                                    (work.charAt(1))) + " and U+" + Integer.toHexString(
+                                    (int)(work.charAt(2))));
+                        errorCount++;
+                        if (errorCount >= 75)
+                            return;
+                    }
+                }
+            }
+        }
+    }
+
+    public void TestCharacterInvariants()
+    {
+        BreakIterator e = BreakIterator.getCharacterInstance();
+        doBreakInvariantTest(e, cannedTestChars + "\u1100\u1101\u1102\u1160\u1161\u1162\u11a8"
+            + "\u11a9\u11aa");
+        doOtherInvariantTest(e, cannedTestChars + "\u1100\u1101\u1102\u1160\u1161\u1162\u11a8"
+            + "\u11a9\u11aa");
+    }
+
+    public void TestEmptyString()
+    {
+        String text = "";
+        Vector x = new Vector();
+        x.addElement(text);
+
+        generalIteratorTest(lineBreak, x);
+    }
+
+    public void TestGetAvailableLocales()
+    {
+        Locale[] locList = BreakIterator.getAvailableLocales();
+
+        if (locList.length == 0)
+            errln("getAvailableLocales() returned an empty list!");
+        // I have no idea how to test this function...
+    }
+
+    /**
+     * @bug 4095322
+     */
+    public void TestJapaneseLineBreak()
+    {
+        StringBuffer testString = new StringBuffer("\u4e00x\u4e8c");
+        String precedingChars = "([{\u00ab$\u00a5\u00a3\u00a4\u2018\u201a\u201c\u201e\u201b\u201f";
+        String followingChars = ")]}\u00bb!%,.\u3001\u3002\u3063\u3083\u3085\u3087\u30c3\u30e3\u30e5\u30e7\u30fc:;\u309b\u309c\u3005\u309d\u309e\u30fd\u30fe\u2019\u201d\u00b0\u2032\u2033\u2034\u2030\u2031\u2103\u2109\u00a2\u0300\u0301\u0302";
+        BreakIterator iter = BreakIterator.getLineInstance(Locale.JAPAN);
+
+        for (int i = 0; i < precedingChars.length(); i++) {
+            testString.setCharAt(1, precedingChars.charAt(i));
+            iter.setText(testString.toString());
+            int j = iter.first();
+            if (j != 0)
+                errln("ja line break failure: failed to start at 0");
+            j = iter.next();
+            if (j != 1)
+                errln("ja line break failure: failed to stop before '" + precedingChars.charAt(i)
+                            + "' (" + ((int)(precedingChars.charAt(i))) + ")");
+            j = iter.next();
+            if (j != 3)
+                errln("ja line break failure: failed to skip position after '" + precedingChars.charAt(i)
+                            + "' (" + ((int)(precedingChars.charAt(i))) + ")");
+        }
+
+        for (int i = 0; i < followingChars.length(); i++) {
+            testString.setCharAt(1, followingChars.charAt(i));
+            iter.setText(testString.toString());
+            int j = iter.first();
+            if (j != 0)
+                errln("ja line break failure: failed to start at 0");
+            j = iter.next();
+            if (j != 2)
+                errln("ja line break failure: failed to skip position before '" + followingChars.charAt(i)
+                            + "' (" + ((int)(followingChars.charAt(i))) + ")");
+            j = iter.next();
+            if (j != 3)
+                errln("ja line break failure: failed to stop after '" + followingChars.charAt(i)
+                            + "' (" + ((int)(followingChars.charAt(i))) + ")");
+        }
+    }
+
+    /**
+     * @bug 4068137
+     */
+    public void TestEndBehavior()
+    {
+        String testString = "boo.";
+        BreakIterator wb = BreakIterator.getWordInstance();
+        wb.setText(testString);
+
+        if (wb.first() != 0)
+            errln("Didn't get break at beginning of string.");
+        if (wb.next() != 3)
+            errln("Didn't get break before period in \"boo.\"");
+        if (wb.current() != 4 && wb.next() != 4)
+            errln("Didn't get break at end of string.");
+    }
+}
+
diff --git a/src/com/ibm/test/rbbi/SimpleBITest.java b/src/com/ibm/test/rbbi/SimpleBITest.java
new file mode 100755
index 0000000..cc92283
--- /dev/null
+++ b/src/com/ibm/test/rbbi/SimpleBITest.java
@@ -0,0 +1,177 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/rbbi/Attic/SimpleBITest.java,v $ 
+ * $Date: 2000/03/10 03:47:46 $ 
+ * $Revision: 1.5 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.rbbi;
+
+import java.util.Locale;
+import com.ibm.text.BreakIterator;
+import com.ibm.test.TestFmwk;
+
+public class SimpleBITest extends TestFmwk{
+    public static final String testText =
+//        "The rain in Spain stays mainly on the plain.  The plains in Spain are mainly pained with rain.";
+//"one-two now--  Hah!  You owe me exactly $1,345.67...  Pay up, huh?  By the way, why don't I send you my re\u0301sume\u0301?  This is a line\r\nbreak.";
+//"nowisthetimeforallgoodmen...  tocometothehelpoftheircountry";
+"When, in the course of human events, it becomes necessary for one people to dissolve the political bonds which have "
+//"When,inthecourseofhumanevents,itbecomesnecessaryforonepeopletodissolvethepoliticalbondswhichhave"
++ "connectedthemwithanother,andtoassumeamongthepowersoftheearth,theseparateandequalstationtowhichthelaws"
++ "ofnatureandofnature'sGodentitlethem,adecentrespecttotheopinionsofmankindrequiresthattheyshoulddeclarethe"
++ "causeswhichimpelthemtotheseparation\n"
++ "Weholdthesetruthstobeself-evident,thatallmenarecreatedequal,thattheyareendowedbytheirCreatorwithcertain"
++ "unalienablerights,thatamongthesearelife,libertyandthepursuitofhappiness.Thattosecuretheserights,governmentsare"
++ "institutedamongmen,derivingtheirjustpowersfromtheconsentofthegoverned.Thatwheneveranyformofgovernment"
++ "becomesdestructivetotheseends,itistherightofthepeopletoalterortoabolishit,andtoinstitutenewgovernment,laying"
++ "itsfoundationonsuchprinciplesandorganizingitspowersinsuchform,astothemshallseemmostlikelytoeffecttheirsafety"
++ "andhappiness.Prudence,indeed,willdictatethatgovernmentslongestablishedshouldnotbechangedforlightandtransient"
++ "causes;andaccordinglyallexperiencehathshownthatmankindaremoredisposedtosuffer,whileevilsaresufferable,than"
++ "torightthemselvesbyabolishingtheformstowhichtheyareaccustomed.Butwhenalongtrainofabusesandusurpations,"
++ "pursuinginvariablythesameobjectevincesadesigntoreducethemunderabsolutedespotism,itistheirright,itistheirduty,"
++ "tothrowoffsuchgovernment,andtoprovidenewguardsfortheirfuturesecurity.--Suchhasbeenthepatientsufferanceof"
++ "thesecolonies;andsuchisnowthenecessitywhichconstrainsthemtoaltertheirformersystemsofgovernment.Thehistory"
++ "ofthepresentKingofGreatBritainisahistoryofrepeatedinjuriesandusurpations,allhavingindirectobjectthe"
++ "establishmentofanabsolutetyrannyoverthesestates.Toprovethis,letfactsbesubmittedtoacandidworld.\n"
++ "Hehasrefusedhisassenttolaws,themostwholesomeandnecessaryforthepublicgood.\n"
++ "Hehasforbiddenhisgovernorstopasslawsofimmediateandpressingimportance,unlesssuspendedintheiroperationtill"
++ "hisassentshouldbeobtained;andwhensosuspended,hehasutterlyneglectedtoattendtothem.\n"
++ "Hehasrefusedtopassotherlawsfortheaccommodationoflargedistrictsofpeople,unlessthosepeoplewouldrelinquish"
++ "therightofrepresentationinthelegislature,arightinestimabletothemandformidabletotyrantsonly.\n"
++ "Hehascalledtogetherlegislativebodiesatplacesunusual,uncomfortable,anddistantfromthedepositoryoftheirpublic"
++ "records,forthesolepurposeoffatiguingthemintocompliancewithhismeasures.\n"
++ "Hehasdissolvedrepresentativehousesrepeatedly,foropposingwithmanlyfirmnesshisinvasionsontherightsofthepeople.\n"
++ "Hehasrefusedforalongtime,aftersuchdissolutions,tocauseotherstobeelected;wherebythelegislativepowers,"
++ "incapableofannihilation,havereturnedtothepeopleatlargefortheirexercise;thestateremaininginthemeantimeexposed"
++ "toallthedangersofinvasionfromwithout,andconvulsionswithin.\n"
++ "Hehasendeavoredtopreventthepopulationofthesestates;forthatpurposeobstructingthelawsfornaturalizationof"
++ "foreigners;refusingtopassotherstoencouragetheirmigrationhither,andraisingtheconditionsofnewappropriationsof"
++ "lands.\n"
++ "Hehasobstructedtheadministrationofjustice,byrefusinghisassenttolawsforestablishingjudiciarypowers.\n"
++ "Hehasmadejudgesdependentonhiswillalone,forthetenureoftheiroffices,andtheamountandpaymentoftheirsalaries.\n"
++ "Hehaserectedamultitudeofnewoffices,andsenthitherswarmsofofficerstoharassourpeople,andeatouttheir"
++ "substance.\n"
++ "Hehaskeptamongus,intimesofpeace,standingarmieswithouttheconsentofourlegislature.\n"
++ "Hehasaffectedtorenderthemilitaryindependentofandsuperiortocivilpower.\n"
++ "Hehascombinedwithotherstosubjectustoajurisdictionforeigntoourconstitution,andunacknowledgedbyourlaws;"
++ "givinghisassenttotheiractsofpretendedlegislation:\n"
++ "Forquarteringlargebodiesofarmedtroopsamongus:\n"
++ "Forprotectingthem,bymocktrial,frompunishmentforanymurderswhichtheyshouldcommitontheinhabitantsofthese"
++ "states:\n"
++ "Forcuttingoffourtradewithallpartsoftheworld:\n"
++ "Forimposingtaxesonuswithoutourconsent:\n"
++ "Fordeprivingusinmanycases,ofthebenefitsoftrialbyjury:\n"
++ "Fortransportingusbeyondseastobetriedforpretendedoffenses:\n"
++ "ForabolishingthefreesystemofEnglishlawsinaneighboringprovince,establishingthereinanarbitrarygovernment,and"
++ "enlargingitsboundariessoastorenderitatonceanexampleandfitinstrumentforintroducingthesameabsoluteruleinthese"
++ "colonies:\n"
++ "Fortakingawayourcharters,abolishingourmostvaluablelaws,andalteringfundamentallytheformsofourgovernments:\n"
++ "Forsuspendingourownlegislatures,anddeclaringthemselvesinvestedwithpowertolegislateforusinallcaseswhatsoever.\n"
++ "Hehasabdicatedgovernmenthere,bydeclaringusoutofhisprotectionandwagingwaragainstus.\n"
++ "Hehasplunderedourseas,ravagedourcoasts,burnedourtowns,anddestroyedthelivesofourpeople.\n"
++ "Heisatthistimetransportinglargearmiesofforeignmercenariestocompletetheworksofdeath,desolationandtyranny,"
++ "alreadybegunwithcircumstancesofcrueltyandperfidyscarcelyparalleledinthemostbarbarousages,andtotalyunworth"
++ "theheadofacivilizednation.\n"
++ "Hehasconstrainedourfellowcitizenstakencaptiveonthehighseastobeararmsagainsttheircountry,tobecomethe"
++ "executionersoftheirfriendsandbrethren,ortofallthemselvesbytheirhands.\n"
++ "Hehasexciteddomesticinsurrectionsamongstus,andhasendeavoredtobringontheinhabitantsofourfrontiers,the"
++ "mercilessIndiansavages,whoseknownruleofwarfare,isundistinguisheddestructionofallages,sexesandconditions.\n"
++ "Ineverystageoftheseoppressionswehavepetitionedforredressinthemosthumbleterms:ourrepeatedpetitionshave"
++ "beenansweredonlybyrepeatedinjury.Aprince,whosecharacteristhusmarkedbyeveryactwhichmaydefineatyrant,is"
++ "unfittobetherulerofafreepeople.\n"
++ "NorhavewebeenwantinginattentiontoourBritishbrethren.Wehavewarnedthemfromtimetotimeofattemptsbytheir"
++ "legislaturetoextendanunwarrantablejurisdictionoverus.Wehaveremindedthemofthecircumstancesofouremigration"
++ "andsettlementhere.Wehaveappealedtotheirnativejusticeandmagnanimity,andwehaveconjuredthembythetiesofour"
++ "commonkindredtodisavowtheseusurpations,which,wouldinevitablyinterruptourconnectionsandcorrespondence.We"
++ "must,therefore,acquiesceinthenecessity,whichdenouncesourseparation,andholdthem,asweholdtherestofmankind,"
++ "enemiesinwar,inpeacefriends.\n"
++ "We,therefore,therepresentativesoftheUnitedStatesofAmerica,inGeneralCongress,assembled,appealingtothe"
++ "SupremeJudgeoftheworldfortherectitudeofourintentions,do,inthename,andbytheauthorityofthegoodpeopleof"
++ "thesecolonies,solemnlypublishanddeclare,thattheseunitedcoloniesare,andofrightoughttobefreeandindependent"
++ "states;thattheyareabsolvedfromallallegiancetotheBritishCrown,andthatallpoliticalconnectionbetweenthemandthe"
++ "stateofGreatBritain,isandoughttobetotallydissolved;andthatasfreeandindependentstates,theyhavefullpowerto"
++ "leveywar,concludepeace,contractalliances,establishcommerce,andtodoallotheractsandthingswhichindependent"
++ "statesmayofrightdo.Andforthesupportofthisdeclaration,withafirmrelianceontheprotectionofDivineProvidence,we"
++ "mutuallypledgetoeachotherourlives,ourfortunesandoursacredhonor.\n";
+
+	public static void main(String[] args) throws Exception {
+		new SimpleBITest().run(args);
+	}
+
+    public void testWordBreak() throws Exception {
+        BreakIterator wordBreak = BreakIterator.getWordInstance(new Locale("en", "US", "TEST"));
+        int breaks = doTest(wordBreak);
+        logln(String.valueOf(breaks));
+	}
+
+    public void testLineBreak() throws Exception {
+        BreakIterator lineBreak = BreakIterator.getLineInstance(new Locale("en", "US", "TEST"));
+        int breaks = doTest(lineBreak);
+        logln(String.valueOf(breaks));
+	}
+
+    public void testSentenceBreak() throws Exception {
+        BreakIterator sentenceBreak = BreakIterator.getSentenceInstance(new Locale("en", "US", "TEST"));
+        int breaks = doTest(sentenceBreak);
+        logln(String.valueOf(breaks));
+    }
+
+    private int doTest(BreakIterator bi) {
+        // forward
+        bi.setText(testText);
+        int p = bi.first();
+        int lastP = p;
+        String fragment;
+        int breaks = 0;
+        logln("Forward...");
+        while (p != BreakIterator.DONE) {
+            p = bi.next();
+            if (p != BreakIterator.DONE) {
+                fragment = testText.substring(lastP, p);
+            } else {
+                fragment = testText.substring(lastP);
+			}
+            debugPrintln(": >" + fragment + "<");
+            ++breaks;
+            lastP = p;
+        }
+        return breaks;
+    }
+
+    private void debugPrintln(String s) {
+        final String zeros = "0000";
+        String temp;
+        StringBuffer out = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c >= ' ' && c < '\u007f')
+                out.append(c);
+            else {
+                out.append("\\u");
+                temp = Integer.toHexString((int)c);
+                out.append(zeros.substring(0, 4 - temp.length()));
+                out.append(temp);
+            }
+        }
+        logln(out.toString());
+    }
+
+    private void debugPrintln2(String s) {
+        StringBuffer out = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c >= '\u0100')
+                out.append("<" + ((int)c - 0x100) + ">");
+            else
+                out.append(c);
+        }
+        logln(out.toString());
+    }
+}
+
diff --git a/src/com/ibm/test/rbbi/english.dict b/src/com/ibm/test/rbbi/english.dict
new file mode 100755
index 0000000..860dcbe
--- /dev/null
+++ b/src/com/ibm/test/rbbi/english.dict
Binary files differ
diff --git a/src/com/ibm/test/rbnf/RbnfRoundTripTest.java b/src/com/ibm/test/rbnf/RbnfRoundTripTest.java
new file mode 100755
index 0000000..2505402
--- /dev/null
+++ b/src/com/ibm/test/rbnf/RbnfRoundTripTest.java
@@ -0,0 +1,220 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/rbnf/Attic/RbnfRoundTripTest.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.rbnf;
+
+import com.ibm.text.RuleBasedNumberFormat;
+import com.ibm.test.TestFmwk;
+
+import java.util.Locale;
+import java.text.NumberFormat;
+
+public class RbnfRoundTripTest extends TestFmwk {
+    /**
+     * Puts a copyright in the .class file
+     */
+    private static final String copyrightNotice
+        = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
+
+    public static void main(String[] args) {
+        RbnfRoundTripTest test = new RbnfRoundTripTest();
+
+        try {
+            test.run(args);
+        }
+        catch (Throwable e) {
+            System.out.println("Entire test failed because of exception: "
+                            + e.toString());
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the English spellout rules
+     */
+    public void TestEnglishSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.US,
+                        RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -12345678, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the duration-formatting rules
+     */
+    public void TestDurationsRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.US,
+                        RuleBasedNumberFormat.DURATION);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Spanish spellout rules
+     */
+    public void TestSpanishSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("es", "es",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -12345678, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the French spellout rules
+     */
+    public void TestFrenchSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.FRANCE,
+                        RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -12345678, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Swiss French spellout rules
+     */
+    public void TestSwissFrenchSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("fr", "CH",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -12345678, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Italian spellout rules
+     */
+    public void TestItalianSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.ITALIAN,
+                        RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -999999, 999999);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the German spellout rules
+     */
+    public void TestGermanSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.GERMANY,
+                        RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Swedish spellout rules
+     */
+    public void TestSwedishSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("sv", "SE",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Dutch spellout rules
+     */
+    public void TestDutchSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("nl", "NL",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, -12345678, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Japanese spellout rules
+     */
+    public void TestJapaneseSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.JAPAN,
+                        RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Russian spellout rules
+     */
+    public void TestRussianSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("ru", "RU",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    /**
+     * Perform an exhaustive round-trip test on the Greek spellout rules
+     */
+    public void TestGreekSpelloutRT() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("el", "GR",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+
+        doTest(formatter, 0, 12345678);
+    }
+
+    void doTest(RuleBasedNumberFormat formatter,  long lowLimit,
+                    long highLimit) {
+        try {
+            long count = 0;
+            long increment = 1;
+            for (long i = lowLimit; i <= highLimit; i += increment) {
+                if (count % 1000 == 0)
+                    logln(Long.toString(i));
+
+                if (Math.abs(i) < 5000)
+                    increment = 1;
+                else if (Math.abs(i) < 500000)
+                    increment = 2737;
+                else
+                    increment = 267437;
+
+                String text = formatter.format(i);
+                long rt = formatter.parse(text).longValue();
+
+                if (rt != i) {
+                    errln("Round-trip failed: " + i + " -> " + text +
+                                    " -> " + rt);
+                }
+
+                ++count;
+            }
+
+            if (lowLimit < 0) {
+                double d = 1.234;
+                while (d < 1000) {
+                    String text = formatter.format(d);
+                    double rt = formatter.parse(text).doubleValue();
+
+                    if (rt != d) {
+                        errln("Round-trip failed: " + d + " -> " + text +
+                                        " -> " + rt);
+                    }
+                    d *= 10;
+                }
+            }
+        }
+        catch (Throwable e) {
+            errln("Test failed with exception: " + e.toString());
+            e.printStackTrace();
+        }
+    }
+}
+
diff --git a/src/com/ibm/test/rbnf/RbnfTest.java b/src/com/ibm/test/rbnf/RbnfTest.java
new file mode 100755
index 0000000..6e9ba34
--- /dev/null
+++ b/src/com/ibm/test/rbnf/RbnfTest.java
@@ -0,0 +1,390 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/rbnf/Attic/RbnfTest.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.rbnf;
+
+import com.ibm.text.RuleBasedNumberFormat;
+import com.ibm.test.TestFmwk;
+
+import java.util.Locale;
+import java.text.NumberFormat;
+
+public class RbnfTest extends TestFmwk {
+    /**
+     * Puts a copyright in the .class file
+     */
+    private static final String copyrightNotice
+        = "Copyright \u00a91997-1999 IBM Corp.  All rights reserved.";
+
+    public static void main(String[] args) {
+        RbnfTest test = new RbnfTest();
+
+        try {
+            test.run(args);
+        }
+        catch (Throwable e) {
+            System.out.println("Entire test failed because of exception: "
+                            + e.toString());
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Perform a simple spot check on the English spellout rules
+     */
+    public void TestEnglishSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.US,
+                        RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "one" },
+            { "15", "fifteen" },
+            { "20", "twenty" },
+            { "23", "twenty-three" },
+            { "73", "seventy-three" },
+            { "88", "eighty-eight" },
+            { "100", "one hundred" },
+            { "106", "one hundred and six" },
+            { "127", "one hundred and twenty-seven" },
+            { "200", "two hundred" },
+            { "579", "five hundred and seventy-nine" },
+            { "1,000", "one thousand" },
+            { "2,000", "two thousand" },
+            { "3,004", "three thousand and four" },
+            { "4,567", "four thousand five hundred and sixty-seven" },
+            { "15,943", "fifteen thousand nine hundred and forty-three" },
+            { "2,345,678", "two million, three hundred and forty-five "
+                        + "thousand, six hundred and seventy-eight" },
+            { "-36", "minus thirty-six" },
+            { "234.567", "two hundred and thirty-four point five six seven" }
+        };
+
+        doTest(formatter, testData, true);
+
+        formatter.setLenientParseMode(true);
+        String[][] lpTestData = {
+            { "FOurhundred     thiRTY six", "436" },
+            { "2 thousand six HUNDRED fifty-7", "2,657" },
+            { "fifteen hundred and zero", "1,500" }
+        };
+        doLenientParseTest(formatter, lpTestData);
+    }
+
+    /**
+     * Perform a simple spot check on the English ordinal-abbreviation rules
+     */
+    public void TestOrdinalAbbreviations() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.US,
+                        RuleBasedNumberFormat.ORDINAL);
+        String[][] testData = {
+            { "1", "1st" },
+            { "2", "2nd" },
+            { "3", "3rd" },
+            { "4", "4th" },
+            { "7", "7th" },
+            { "10", "10th" },
+            { "11", "11th" },
+            { "13", "13th" },
+            { "20", "20th" },
+            { "21", "21st" },
+            { "22", "22nd" },
+            { "23", "23rd" },
+            { "24", "24th" },
+            { "33", "33rd" },
+            { "102", "102nd" },
+            { "312", "312th" },
+            { "12,345", "12,345th" }
+        };
+
+        doTest(formatter, testData, false);
+    }
+
+    /**
+     * Perform a simple spot check on the duration-formatting rules
+     */
+    public void TestDurations() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.US,
+                        RuleBasedNumberFormat.DURATION);
+        String[][] testData = {
+            { "3,600", "1:00:00" },		//move me and I fail
+            { "0", "0 sec." },
+            { "1", "1 sec." },
+            { "24", "24 sec." },
+            { "60", "1:00" },
+            { "73", "1:13" },
+            { "145", "2:25" },
+            { "666", "11:06" },
+//            { "3,600", "1:00:00" },
+            { "3,740", "1:02:20" },
+            { "10,293", "2:51:33" }
+        };
+
+        doTest(formatter, testData, true);
+
+        formatter.setLenientParseMode(true);
+        String[][] lpTestData = {
+            { "2-51-33", "10,293" }
+        };
+        doLenientParseTest(formatter, lpTestData);
+    }
+
+    /**
+     * Perform a simple spot check on the Spanish spellout rules
+     */
+    public void TestSpanishSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("es", "es",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "uno" },
+            { "6", "seis" },
+            { "16", "diecis\u00e9is" },
+            { "20", "veinte" },
+            { "24", "veinticuatro" },
+            { "26", "veintis\u00e9is" },
+            { "73", "setenta y tres" },
+            { "88", "ochenta y ocho" },
+            { "100", "cien" },
+            { "106", "ciento seis" },
+            { "127", "ciento veintisiete" },
+            { "200", "doscientos" },
+            { "579", "quinientos setenta y nueve" },
+            { "1,000", "mil" },
+            { "2,000", "dos mil" },
+            { "3,004", "tres mil cuatro" },
+            { "4,567", "cuatro mil quinientos sesenta y siete" },
+            { "15,943", "quince mil novecientos cuarenta y tres" },
+            { "2,345,678", "dos mill\u00f3n trescientos cuarenta y cinco mil "
+                    + "seiscientos setenta y ocho"},
+            { "-36", "menos treinta y seis" },
+            { "234.567", "doscientos treinta y cuatro punto cinco seis siete" }
+        };
+
+        doTest(formatter, testData, true);
+    }
+
+    /**
+     * Perform a simple spot check on the French spellout rules
+     */
+    public void TestFrenchSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.FRANCE,
+                        RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "un" },
+            { "15", "quinze" },
+            { "20", "vingt" },
+            { "21", "vingt-et-un" },
+            { "23", "vingt-trois" },
+            { "62", "soixante-deux" },
+            { "70", "soixante-dix" },
+            { "71", "soixante et onze" },
+            { "73", "soixante-treize" },
+            { "80", "quatre-vingts" },
+            { "88", "quatre-vingt-huit" },
+            { "100", "cent" },
+            { "106", "cent six" },
+            { "127", "cent vingt-sept" },
+            { "200", "deux cents" },
+            { "579", "cinq cents soixante-dix-neuf" },
+            { "1,000", "mille" },
+            { "1,123", "onze cents vingt-trois" },
+            { "1,594", "mille cinq cents quatre-vingt-quatorze" },
+            { "2,000", "deux mille" },
+            { "3,004", "trois mille quatre" },
+            { "4,567", "quatre mille cinq cents soixante-sept" },
+            { "15,943", "quinze mille neuf cents quarante-trois" },
+            { "2,345,678", "deux million trois cents quarante-cinq mille "
+                        + "six cents soixante-dix-huit" },
+            { "-36", "moins trente-six" },
+            { "234.567", "deux cents trente-quatre virgule cinq six sept" }
+        };
+
+        doTest(formatter, testData, true);
+
+        formatter.setLenientParseMode(true);
+        String[][] lpTestData = {
+            { "trente-un", "31" },
+            { "un cents quatre vingt dix huit", "198" }
+        };
+        doLenientParseTest(formatter, lpTestData);
+    }
+
+    /**
+     * Perform a simple spot check on the Swiss French spellout rules
+     */
+    public void TestSwissFrenchSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(new Locale("fr", "CH",
+                        ""), RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "un" },
+            { "15", "quinze" },
+            { "20", "vingt" },
+            { "21", "vingt-et-un" },
+            { "23", "vingt-trois" },
+            { "62", "soixante-deux" },
+            { "70", "septante" },
+            { "71", "septante-et-un" },
+            { "73", "septante-trois" },
+            { "80", "octante" },
+            { "88", "octante-huit" },
+            { "100", "cent" },
+            { "106", "cent six" },
+            { "127", "cent vingt-sept" },
+            { "200", "deux cents" },
+            { "579", "cinq cents septante-neuf" },
+            { "1,000", "mille" },
+            { "1,123", "onze cents vingt-trois" },
+            { "1,594", "mille cinq cents nonante-quatre" },
+            { "2,000", "deux mille" },
+            { "3,004", "trois mille quatre" },
+            { "4,567", "quatre mille cinq cents soixante-sept" },
+            { "15,943", "quinze mille neuf cents quarante-trois" },
+            { "2,345,678", "deux million trois cents quarante-cinq mille "
+                        + "six cents septante-huit" },
+            { "-36", "moins trente-six" },
+            { "234.567", "deux cents trente-quatre virgule cinq six sept" }
+        };
+
+        doTest(formatter, testData, true);
+    }
+
+    /**
+     * Perform a simple spot check on the Italian spellout rules
+     */
+    public void TestItalianSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.ITALIAN,
+                        RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "uno" },
+            { "15", "quindici" },
+            { "20", "venti" },
+            { "23", "ventitre" },
+            { "73", "settantatre" },
+            { "88", "ottantotto" },
+            { "100", "cento" },
+            { "106", "centosei" },
+            { "108", "centotto" },
+            { "127", "centoventisette" },
+            { "181", "centottantuno" },
+            { "200", "duecento" },
+            { "579", "cinquecentosettantanove" },
+            { "1,000", "mille" },
+            { "2,000", "duemila" },
+            { "3,004", "tremilaquattro" },
+            { "4,567", "quattromilacinquecentosessantasette" },
+            { "15,943", "quindicimilanovecentoquarantatre" },
+            { "-36", "meno trentisei" },
+            { "234.567", "duecentotrentiquattro virgola cinque sei sette" }
+        };
+
+        doTest(formatter, testData, true);
+    }
+
+    /**
+     * Perform a simple spot check on the German spellout rules
+     */
+    public void TestGermanSpellout() {
+        RuleBasedNumberFormat formatter
+                        = new RuleBasedNumberFormat(Locale.GERMANY,
+                        RuleBasedNumberFormat.SPELLOUT);
+        String[][] testData = {
+            { "1", "eins" },
+            { "15", "f\u00fcnfzehn" },
+            { "20", "zwanzig" },
+            { "23", "dreiundzwanzig" },
+            { "73", "dreiundsiebzig" },
+            { "88", "achtundachtzig" },
+            { "100", "hundert" },
+            { "106", "hundertsechs" },
+            { "127", "hundertsiebenundzwanzig" },
+            { "200", "zweihundert" },
+            { "579", "f\u00fcnfhundertneunundsiebzig" },
+            { "1,000", "tausend" },
+            { "2,000", "zweitausend" },
+            { "3,004", "dreitausendvier" },
+            { "4,567", "viertausendf\u00fcnfhundertsiebenundsechzig" },
+            { "15,943", "f\u00fcnfzehntausendneunhundertdreiundvierzig" },
+            { "2,345,678", "zwei Millionen dreihundertf\u00fcnfundvierzigtausend"
+                        + "sechshundertachtundsiebzig" }
+        };
+
+        doTest(formatter, testData, true);
+
+        formatter.setLenientParseMode(true);
+        String[][] lpTestData = {
+            { "ein Tausend sechs Hundert fuenfunddreissig", "1,635" }
+        };
+        doLenientParseTest(formatter, lpTestData);
+    }
+
+    void doTest(RuleBasedNumberFormat formatter, String[][] testData,
+                boolean testParsing) {
+        NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
+
+        try {
+            for (int i = 0; i < testData.length; i++) {
+                String number = testData[i][0];
+                String expectedWords = testData[i][1];
+                String actualWords = formatter.format(decFmt.parse(number));
+
+                if (!actualWords.equals(expectedWords)) {
+                    errln("Spot check failed: for " + number + ", expected "
+                                + expectedWords + ", but got " +
+                                actualWords);
+                }
+                else if (testParsing) {
+                    String actualNumber = decFmt.format(formatter
+                                    .parse(actualWords));
+
+                    if (!actualNumber.equals(number)) {
+                        errln("Spot check failed: for " + actualWords +
+                                ", expected " + number + ", but got " +
+                                actualNumber);
+                    }
+                }
+            }
+        }
+        catch (Throwable e) {
+            errln("Test failed with exception: " + e.toString());
+            e.printStackTrace();
+        }
+    }
+
+    void doLenientParseTest(RuleBasedNumberFormat formatter,
+                    String[][] testData) {
+        NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
+
+        try {
+            for (int i = 0; i < testData.length; i++) {
+                String words = testData[i][0];
+                String expectedNumber = testData[i][1];
+                String actualNumber = decFmt.format(formatter.parse(words));
+
+                if (!actualNumber.equals(expectedNumber)) {
+                    errln("Lenient-parse spot check failed: for "
+                                + words + ", expected " + expectedNumber
+                                + ", but got " + actualNumber);
+                }
+            }
+        }
+        catch (Throwable e) {
+            errln("Test failed with exception: " + e.toString());
+            e.printStackTrace();
+        }
+    }
+}
+
diff --git a/src/com/ibm/test/search/SearchTest.java b/src/com/ibm/test/search/SearchTest.java
new file mode 100755
index 0000000..22286f1
--- /dev/null
+++ b/src/com/ibm/test/search/SearchTest.java
@@ -0,0 +1,463 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/search/Attic/SearchTest.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.2 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.search;
+
+import java.text.*;
+import java.util.*;
+
+//import com.ibm.text.*;
+import com.ibm.text.SearchIterator;
+import com.ibm.text.StringSearch;
+
+/**
+ * Unit and regression tests for the StringSearch and SearchIterator classes.
+ * This uses <code>IntlTest</code> as a framework for running the tests
+ * and displaying the output.  Basically, any method here that starts with
+ * <code>Test</code> is run as a test.
+ */
+public class SearchTest extends com.ibm.test.TestFmwk {
+    public static void main(String[] args) throws Exception {
+        new SearchTest().run(args);
+    }
+    
+    //-----------------------------------------------------------
+    // Static data: collators and break iterators to use for testing
+    //
+    static RuleBasedCollator enColl;        // Generic English collator
+    static RuleBasedCollator frColl;        // French accent rules
+    static RuleBasedCollator esColl;        // Has Spanish contracting "ch"
+    static RuleBasedCollator deColl;        // Has expansions, e.g. a-umlaut -> ae
+    
+    static {
+        try {
+            enColl = (RuleBasedCollator)Collator.getInstance(Locale.US);
+            frColl = (RuleBasedCollator)Collator.getInstance(Locale.FRANCE);
+            
+            esColl = new RuleBasedCollator(enColl.getRules() + " & C < ch ; cH ; Ch ; CH");
+            
+            deColl = new RuleBasedCollator(enColl.getRules() + " & ae ; ä & AE ; Ä"
+                                                             + " & oe ; ö & OE ; Ö"
+                                                             + " & ue ; ü & UE ; Ü"); 
+        }
+        catch (ParseException e) {
+        }
+    }
+    
+    static BreakIterator enWord = BreakIterator.getWordInstance(Locale.US);
+    
+    static String testString = 
+            "blackbirds Pat p\u00E9ch\u00E9 " +
+            "p\u00EAche p\u00E9cher p\u00EAcher " +
+            "Tod T\u00F6ne black Tofu blackbirds " +
+            "Ton PAT toehold " +
+            "blackbird " +
+            "black-bird pat " +
+            "toe big Toe";
+    
+    //-------------------------------------------------------------------------
+    // The primary test consists of running through all of the strings in this
+    // table and making sure we find the proper matches
+    //
+    static class TestCase {
+        TestCase(RuleBasedCollator c, int strength, BreakIterator breaker,
+                    String pattern, String target, int[] matches) {
+            this.collator = c;
+            this.strength = strength;
+            this.breaker = breaker;
+            this.pattern = pattern;
+            this.target = target;
+            this.matches = matches;
+        }
+        RuleBasedCollator   collator;
+        int                 strength;
+        BreakIterator       breaker;
+        String              pattern;
+        String              target;
+        int[]               matches;
+    };
+    static TestCase[] testCases = {
+        new TestCase(enColl, Collator.PRIMARY, null, "fox",
+                //   012345678901234567890123456789012345678901234567890123456789
+                    "The quick brown fox jumps over the lazy foxes",
+                    new int[] { 16, 40 }
+                    ),
+
+        new TestCase(enColl, Collator.PRIMARY, enWord, "fox",
+                //   012345678901234567890123456789012345678901234567890123456789
+                    "The quick brown fox jumps over the lazy foxes",
+                    new int[] { 16 }
+                    ),
+
+        new TestCase(frColl, Collator.PRIMARY, null, "peche",
+                    testString,
+                    new int[] { 15, 21, 27, 34 }
+                    ),
+        new TestCase(frColl, Collator.PRIMARY, enWord, "blackbird",
+                    testString,
+                    new int[] { 88, 98 }
+                    ),
+
+        // NOTE: this case depends on a bug fix in JDK 1.2.2 ("Cricket")
+        new TestCase(deColl, Collator.PRIMARY, null, "toe",
+                //   012345678901234567890123456789012345678901234567890123456789
+                    "This is a toe T\u00F6ne",
+                    new int[] { 10, 14 }
+                    ),
+
+        /* Due to a bug in the JDK 1.2 FCS version of CollationElementIterator,
+         * searching through text containing contracting character sequences 
+         * isn't working properly right now.  This will probably be fixed in
+         * JDK 1.3 ("Kestrel").  When it is, uncomment these test cases.
+         *
+        new TestCase(esColl, Collator.PRIMARY, enWord, "channel",
+                //   0123456789012345678901234567890123456789012345678901234567890123456789
+                    "A channel, another CHANNEL, more Channels, and one last channel...",
+                    new int[] {  }
+                    ),
+
+        new TestCase(esColl, Collator.TERTIARY, enWord, "Channel",
+                //   0123456789012345678901234567890123456789012345678901234567890123456789
+                    "Channel, another channel, more channels, and one last Channel",
+                    new int[] {  }
+                    ),
+        */
+
+    };
+
+    /**
+     * Test using the test cases defined above
+     */
+    public void TestCases() {
+        for (int t = 0; t < testCases.length; t++)
+        {
+            logln("case " + t);
+            TestCase c = testCases[t];
+            StringSearch iter = new StringSearch(c.pattern,
+                                        new StringCharacterIterator(c.target),
+                                        c.collator, c.breaker);
+            iter.setStrength(c.strength);
+            doTestCase(iter, c.matches);
+        }
+    }
+    
+    /**
+     * Test for SearchIterator.setOverlapping()
+     */
+    public void TestOverlapping() {
+        // Create a search iterator. 
+        StringSearch iter = new StringSearch("abab",
+                                             new StringCharacterIterator("abababab"),
+                                             enColl, null);
+        
+        int[] overlap = new int[] { 0, 2, 4 };  // expected results
+        int[] novrlap = new int[] { 0, 4 };
+
+        
+        doTestCase(iter, overlap);          // Overlapping is allowed by default
+        if (iter.isOverlapping() != true) {
+            errln("ERROR: isOverlapping returned " + iter.isOverlapping());
+        }
+        
+        iter.setOverlapping(false);         // Turn 'em back off
+        doTestCase(iter, novrlap);
+        if (iter.isOverlapping() != false) {
+            errln("ERROR: isOverlapping returned " + iter.isOverlapping());
+        }
+
+        iter.setOverlapping(true);
+        doTestCase(iter, overlap);
+        if (iter.isOverlapping() != true) {
+            errln("ERROR: isOverlapping returned " + iter.isOverlapping());
+        }
+    }
+    
+    /**
+     * Test for SearchIterator.setBreakIterator
+     */
+    public void TestBreakIterator() {
+        StringSearch iter = new StringSearch("fox",
+                                 new StringCharacterIterator("foxy fox"),
+                                 enColl, null);
+
+        BreakIterator charBreaker = BreakIterator.getCharacterInstance(Locale.US);
+        BreakIterator wordBreaker = BreakIterator.getWordInstance(Locale.US);
+        
+        int[] chars = new int[] { 0, 5 };   // expected results
+        int[] words = new int[] { 5 };
+        
+        logln("default break iterator...");
+        doTestCase(iter, chars);            // character breaker by default
+        
+        logln("word break iterator...");
+        iter.setBreakIterator(wordBreaker); // word break detection
+        doTestCase(iter, words);
+        if (iter.getBreakIterator() != wordBreaker) {
+            errln("ERROR: getBreakIterator returned wrong object");
+        }
+
+        logln("char break iterator...");
+        iter.setBreakIterator(charBreaker); // char break detection
+        doTestCase(iter, chars);
+        if (iter.getBreakIterator() != charBreaker) {
+            errln("ERROR: getBreakIterator returned wrong object");
+        }
+
+        logln("null break iterator...");
+        iter.setBreakIterator(null);
+        doTestCase(iter, chars);
+        if (iter.getBreakIterator() != null) {
+            errln("ERROR: getBreakIterator returned wrong object");
+        }
+    }
+    
+    /**
+     * Test for SearchIterator.setTarget
+     */
+    public void TestSetTarget() {
+        String  pat = "fox";
+        String  targ1 = "the foxy brown fox";
+        String  targ2 = "the quick brown fox";
+        
+        int[] match1 = new int[] { 4, 15 };   // expected results
+        int[] match2 = new int[] { 16 };
+
+        StringSearch iter = new StringSearch(pat, new StringCharacterIterator(targ1),
+                                 enColl, null);
+
+        logln("initial text...");
+        doTestCase(iter, match1);
+        assertEqual(iter.getTarget(), targ1);
+        
+        logln("target #2...");
+        iter.setTarget(new StringCharacterIterator(targ2));
+        doTestCase(iter, match2);
+        assertEqual(iter.getTarget(), targ2);
+        
+        logln("back to target #1...");
+        iter.setTarget(new StringCharacterIterator(targ1));
+        doTestCase(iter, match1);
+        assertEqual(iter.getTarget(), targ1);
+    }
+    
+    /**
+     * Test for StringSearch.setStrength
+     */
+    public void TestSetStrength() {
+        String  pat = "fox";
+        String  targ = "the foxy brown Fox";
+        
+        int[] match1 = new int[] { 4, 15 };   // expected results
+        int[] match3 = new int[] { 4 };
+
+        StringSearch iter = new StringSearch(pat, new StringCharacterIterator(targ),
+                                 enColl, null);
+
+       /* logln("Trying primary strength...");
+        iter.setStrength(Collator.PRIMARY);
+        doTestCase(iter, match1);
+        if (iter.getStrength() != Collator.PRIMARY) {
+            errln("ERROR: getStrength: expected PRIMARY, got " + iter.getStrength());
+        } */
+        
+        logln("Trying tertiary strength...");
+        iter.setStrength(Collator.TERTIARY);
+        doTestCase(iter, match3);
+        if (iter.getStrength() != Collator.TERTIARY) {
+            errln("ERROR: getStrength: expected PRIMARY, got " + iter.getStrength());
+        }
+        
+    }
+
+    /**
+     * Test for StringSearch.setCollator
+     */
+    public void TestSetCollator() throws ParseException {
+        // Create a test collator that thinks "o" and "p" are the same thing
+        RuleBasedCollator testColl = new RuleBasedCollator(enColl.getRules()
+                                            + "& o,O ; p,P" );
+                                            
+        String  pat = "fox";
+        String  targ = "fox fpx ";
+        
+        int[] match1 = new int[] { 0 };     // English results
+        int[] match2 = new int[] { 0, 4 };  // Test collator results
+
+        StringSearch iter = new StringSearch(pat, new StringCharacterIterator(targ),
+                                 enColl, null);
+
+        logln("Trying English collator...");
+
+        iter.setStrength(Collator.PRIMARY);
+        doTestCase(iter, match1);
+        if (iter.getCollator() != enColl) {
+            errln("ERROR: getCollator returned wrong collator");
+        }
+        
+        logln("Trying test collator...");
+
+        iter.setCollator(testColl);
+        iter.setStrength(Collator.PRIMARY);
+        doTestCase(iter, match2);
+        if (iter.getCollator() != testColl) {
+            errln("ERROR: getCollator returned wrong collator");
+        }
+        
+        logln("Trying English collator again...");
+
+        iter.setCollator(enColl);
+        iter.setStrength(Collator.PRIMARY);
+        doTestCase(iter, match1);
+        if (iter.getCollator() != enColl) {
+            errln("ERROR: getCollator returned wrong collator");
+        }
+        
+    }
+
+    /**
+     * Test for StringSearch.setPattern
+     */
+    public void TestSetPattern() {
+                      // 01234567890123456789012345678901234567890123456789
+        String target = "The quick brown fox jumps over the lazy foxes";
+        String pat1 = "the";
+        String pat2 = "fox";
+        
+        int[] match1 = new int[] { 0, 31 };
+        int[] match2 = new int[] { 16, 40 };
+
+        StringSearch iter = new StringSearch(pat1, new StringCharacterIterator(target),
+                                 enColl, null);
+        iter.setStrength(Collator.PRIMARY);
+        
+        doTestCase(iter, match1);
+        if (!iter.getPattern().equals(pat1)) {
+            errln("getPattern returned '" + iter.getPattern() + "', expected '"
+                + pat1 + "'");
+        }
+        
+        iter.setPattern(pat2);
+        doTestCase(iter, match2);
+        if (!iter.getPattern().equals(pat2)) {
+            errln("getPattern returned '" + iter.getPattern() + "', expected '"
+                + pat1 + "'");
+        }
+        
+        iter.setPattern(pat1);
+        doTestCase(iter, match1);
+        if (!iter.getPattern().equals(pat1)) {
+            errln("getPattern returned '" + iter.getPattern() + "', expected '"
+                + pat1 + "'");
+        }
+    }
+    
+    /**
+     * Test for an infinite loop that happened when the target text started
+     * with an ignorable character.
+     * Reported by Muly Oved, <mulyoved@netvision.net.il>
+     */
+    public void TestIgnorableLoop() {
+        String pattern = "go";
+        String target  = "  on";
+
+        StringSearch search;
+        
+        try {
+            search=new StringSearch(pattern, new StringCharacterIterator(target), enColl);
+                                                
+            logln("searching... "+pattern);
+            search.first();
+            logln("Will never go here if searching for 'go'");
+        } catch (Exception e) {
+            errln("Caught exception: " + e.toString());
+        }
+
+        System.out.println("end");
+    }
+    
+    //-------------------------------------------------------------------------
+    // Various internal utility methods....
+    //-------------------------------------------------------------------------
+
+    void assertEqual(CharacterIterator i1, String s2) {
+        CharacterIterator i2 = new StringCharacterIterator(s2);
+        char c1 = i1.first();
+        char c2 = i2.first();
+        int i = 0;
+        
+        while (c1 == c2 && c1 != CharacterIterator.DONE) {
+            c1 = i1.next();
+            c2 = i2.next();
+        }
+        if (c1 != CharacterIterator.DONE || c2 != CharacterIterator.DONE) {
+            errln("CharacterIterator mismatch at index " + i);
+        }
+    }
+    
+    void doTestCase(StringSearch iter, int[] expected) {
+        //
+        // The basic logic here is as follows...  We construct a search
+        // iterator and use it to find all of the matches in the target
+        // text.  Then we compare it to the expected matches
+        //
+        Vector matches = new Vector();
+
+        for (int i = iter.first(); i != SearchIterator.DONE; i = iter.next()) {
+            matches.addElement(new Integer(i));
+        }
+        compareMatches(expected, matches);
+        
+        // Now do the same exact thing as above, but in reverse
+        logln("Now searching in reverse...");
+        matches.removeAllElements();
+        for (int i = iter.last(); i != SearchIterator.DONE; i = iter.previous()) {
+            matches.insertElementAt(new Integer(i), 0);
+        }
+        compareMatches(expected, matches);
+    }
+    
+    /**
+     * Utility function used by TestCases to compare the matches that
+     * were found against the ones that were expected
+     */
+    void compareMatches(int[] expected, Vector found) {
+        // Step through the two arrays in parallel and make sure that they're
+        // the same
+        
+        int e=0, f=0;
+        
+        while (e < expected.length && f < found.size()) {
+            int eVal = expected[e];
+            int fVal = ((Integer)found.elementAt(f)).intValue();
+            
+            if (eVal < fVal) {
+                errln("Missed expected match at " + eVal);
+                e++;
+            } else if (eVal > fVal) {
+                errln("Found unexpected match at " + fVal);
+                f++;
+            } else {
+                e++;
+                f++;
+            }
+        }
+        while (e < expected.length) {
+            errln("Missed expected match at " + expected[e]);
+            e++;
+        }
+        while (f < found.size()) {
+            int fVal = ((Integer)found.elementAt(f)).intValue();
+            errln("Found unexpected match at " + fVal);
+            f++;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/ibm/test/topleveltest/TestAll.java b/src/com/ibm/test/topleveltest/TestAll.java
new file mode 100755
index 0000000..050aa66
--- /dev/null
+++ b/src/com/ibm/test/topleveltest/TestAll.java
@@ -0,0 +1,76 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/topleveltest/Attic/TestAll.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.8 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.topleveltest;
+import com.ibm.test.TestFmwk;
+import java.text.*;
+import java.util.*;
+
+/**
+ * @test
+ * @summary General test of UnicodeSet
+ */
+ 
+public class TestAll extends TestFmwk {
+
+    public static void main(String[] args) throws Exception {
+        new TestAll().run(args);
+    }
+
+    public void TestBigNumberFormat() throws Exception{
+        run(new com.ibm.test.bnf.BigNumberFormatTest());
+    }
+    
+    public void TestCompression1() throws Exception{
+        run(new com.ibm.test.compression.DecompressionTest());
+    }
+    
+    public void TestCompression2() throws Exception{
+        run(new com.ibm.test.compression.ExhaustiveTest());
+    }
+
+	public void TestNormalizer1() throws Exception{
+		run(new com.ibm.test.normalizer.BasicTest());
+	}
+
+	public void TestNormalizer2() throws Exception {
+		run(new com.ibm.test.normalizer.ExhaustiveTest());
+	}
+
+	public void TestRuleBasedNumberFormat1() throws Exception {
+		run(new com.ibm.test.rbnf.RbnfTest());
+	}
+
+	public void TestRulebasedNumberFormat2() throws Exception {
+		run(new com.ibm.test.rbnf.RbnfRoundTripTest());
+	}
+
+	public void TestRuleBasedBreakIteartor1() throws Exception {
+		run(new com.ibm.test.rbbi.SimpleBITest());
+	}
+
+	public void TestRuleBasedBreakIteartor2() throws Exception {
+		run(new com.ibm.test.rbbi.BreakIteratorTest());
+	}
+
+	public void TestTranslit1() throws Exception {
+		run(new com.ibm.test.translit.UnicodeSetTest());
+	}
+
+	public void TestTranslit2() throws Exception {
+		run(new com.ibm.test.translit.TransliteratorTest());
+	}
+	
+	public void TestSearch() throws Exception {
+		run(new com.ibm.test.search.SearchTest());
+	}
+}
diff --git a/src/com/ibm/test/translit/TransliteratorTest.java b/src/com/ibm/test/translit/TransliteratorTest.java
new file mode 100755
index 0000000..72b76c8
--- /dev/null
+++ b/src/com/ibm/test/translit/TransliteratorTest.java
@@ -0,0 +1,470 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/translit/Attic/TransliteratorTest.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.13 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.translit;
+import com.ibm.text.*;
+import com.ibm.test.*;
+import com.ibm.util.Utility;
+import java.text.*;
+import java.util.*;
+
+/**
+ * @test
+ * @summary General test of Transliterator
+ */
+public class TransliteratorTest extends TestFmwk {
+
+    public static void main(String[] args) throws Exception {
+        new TransliteratorTest().run(args);
+    }
+
+    public void TestInstantiation() {
+        long ms = System.currentTimeMillis();
+        String ID;
+        for (Enumeration e = Transliterator.getAvailableIDs(); e.hasMoreElements(); ) {
+            ID = (String) e.nextElement();
+            try {
+                Transliterator t = Transliterator.getInstance(ID);
+                // We should get a new instance if we try again
+                Transliterator t2 = Transliterator.getInstance(ID);
+                if (t != t2) {
+                    logln(ID + ":" + t);
+                } else {
+                    errln("FAIL: " + ID + " returned identical instances");
+                }
+            } catch (IllegalArgumentException ex) {
+                errln("FAIL: " + ID);
+                throw ex;
+            }
+        }
+
+        // Now test the failure path
+        try {
+            ID = "<Not a valid Transliterator ID>";
+            Transliterator t = Transliterator.getInstance(ID);
+            errln("FAIL: " + ID + " returned " + t);
+        } catch (IllegalArgumentException ex) {
+            logln("OK: Bogus ID handled properly");
+        }
+        
+        ms = System.currentTimeMillis() - ms;
+        logln("Elapsed time: " + ms + " ms");
+    }
+
+    public void TestDisplayName() {
+        String ID;
+        for (Enumeration e = Transliterator.getAvailableIDs(); e.hasMoreElements(); ) {
+            ID = (String) e.nextElement();
+            logln(ID + " -> " + Transliterator.getDisplayName(ID));
+        }
+    }
+
+    public void TestSimpleRules() {
+        /* Example: rules 1. ab>x|y
+         *                2. yc>z
+         *
+         * []|eabcd  start - no match, copy e to tranlated buffer
+         * [e]|abcd  match rule 1 - copy output & adjust cursor
+         * [ex|y]cd  match rule 2 - copy output & adjust cursor
+         * [exz]|d   no match, copy d to transliterated buffer
+         * [exzd]|   done
+         */
+        expect("ab>x|y;" +
+               "yc>z",
+               "eabcd", "exzd");
+
+        /* Another set of rules:
+         *    1. ab>x|yzacw
+         *    2. za>q
+         *    3. qc>r
+         *    4. cw>n
+         *
+         * []|ab       Rule 1
+         * [x|yzacw]   No match
+         * [xy|zacw]   Rule 2
+         * [xyq|cw]    Rule 4
+         * [xyqn]|     Done
+         */
+        expect("ab>x|yzacw;" +
+               "za>q;" +
+               "qc>r;" +
+               "cw>n",
+               "ab", "xyqn");
+
+        /* Test categories
+         */
+        Transliterator t = new RuleBasedTransliterator("<ID>",
+                                                       "dummy=\uE100;" +
+                                                       "vowel=[aeiouAEIOU];" +
+                                                       "lu=[:Lu:];" +
+                                                       "{vowel} ({lu}) > !;" +
+                                                       "{vowel} > &;" +
+                                                       "(!) {lu} > ^;" +
+                                                       "{lu} > *;" +
+                                                       "a>ERROR");
+        expect(t, "abcdefgABCDEFGU", "&bcd&fg!^**!^*&");
+    }
+
+    /**
+     * Test undefined variable.
+     */
+    public void TestUndefinedVariable() {
+        String rule = "({initial}) a <> \u1161;";
+        try {
+            Transliterator t = new RuleBasedTransliterator("<ID>", rule);
+        } catch (IllegalArgumentException e) {
+            logln("OK: Got exception for " + rule + ", as expected: " +
+                  e.getMessage());
+            return;
+        }
+        errln("Fail: bogus rule " + rule + " compiled without error");
+    }
+
+    /**
+     * Test empty context.
+     */
+    public void TestEmptyContext() {
+        expect("() a () > b;", "xay a ", "xby b ");
+    }
+
+    /**
+     * Test inline set syntax and set variable syntax.
+     */
+    public void TestInlineSet() {
+        expect("[:Ll:] (x) > y; [:Ll:] > z;", "aAbxq", "zAyzz");
+        expect("a[0-9]b > qrs", "1a7b9", "1qrs9");
+
+        expect("digit = [0-9];" +
+               "alpha = [a-zA-Z];" +
+               "alphanumeric = [{digit}{alpha}];" + // ***
+               "special = [^{alphanumeric}];" +     // ***
+               "{alphanumeric} > -;" +
+               "{special} > *;",
+
+               "thx-1138", "---*----");
+    }
+
+    /**
+     * Create some inverses and confirm that they work.  We have to be
+     * careful how we do this, since the inverses will not be true
+     * inverses -- we can't throw any random string at the composition
+     * of the transliterators and expect the identity function.  F x
+     * F' != I.  However, if we are careful about the input, we will
+     * get the expected results.
+     */
+    public void TestRuleBasedInverse() {
+        String RULES =
+            "abc>zyx;" +
+            "ab>yz;" +
+            "bc>zx;" +
+            "ca>xy;" +
+            "a>x;" +
+            "b>y;" +
+            "c>z;" +
+
+            "abc<zyx;" +
+            "ab<yz;" +
+            "bc<zx;" +
+            "ca<xy;" +
+            "a<x;" +
+            "b<y;" +
+            "c<z;" +
+
+            "";
+
+        String[] DATA = {
+            // Careful here -- random strings will not work.  If we keep
+            // the left side to the domain and the right side to the range
+            // we will be okay though (left, abc; right xyz).
+            "a", "x",
+            "abcacab", "zyxxxyy",
+            "caccb", "xyzzy",
+        };
+
+        Transliterator fwd = new RuleBasedTransliterator("<ID>", RULES);
+        Transliterator rev = new RuleBasedTransliterator("<ID>", RULES,
+                                     RuleBasedTransliterator.REVERSE, null);
+        for (int i=0; i<DATA.length; i+=2) {
+            expect(fwd, DATA[i], DATA[i+1]);
+            expect(rev, DATA[i+1], DATA[i]);
+        }
+    }
+
+    /**
+     * Basic test of keyboard.
+     */
+    public void TestKeyboard() {
+        Transliterator t = new RuleBasedTransliterator("<ID>", 
+                                                       "psch>Y;"
+                                                       +"ps>y;"
+                                                       +"ch>x;"
+                                                       +"a>A;");
+        String DATA[] = {
+            // insertion, buffer
+            "a", "A",
+            "p", "Ap",
+            "s", "Aps",
+            "c", "Apsc",
+            "a", "AycA",
+            "psch", "AycAY",
+            null, "AycAY", // null means finishKeyboardTransliteration
+        };
+
+        keyboardAux(t, DATA);
+    }
+
+    /**
+     * Basic test of keyboard with cursor.
+     */
+    public void TestKeyboard2() {
+        Transliterator t = new RuleBasedTransliterator("<ID>", 
+                                                       "ych>Y;"
+                                                       +"ps>|y;"
+                                                       +"ch>x;"
+                                                       +"a>A;");
+        String DATA[] = {
+            // insertion, buffer
+            "a", "A",
+            "p", "Ap",
+            "s", "Ay",
+            "c", "Ayc",
+            "a", "AycA",
+            "p", "AycAp",
+            "s", "AycAy",
+            "c", "AycAyc",
+            "h", "AycAY",
+            null, "AycAY", // null means finishKeyboardTransliteration
+        };
+
+        keyboardAux(t, DATA);
+    }
+
+    /**
+     * Test keyboard transliteration with back-replacement.
+     */
+    public void TestKeyboard3() {
+        // We want th>z but t>y.  Furthermore, during keyboard
+        // transliteration we want t>y then yh>z if t, then h are
+        // typed.
+        String RULES =
+            "t>|y;" +
+            "yh>z;" +
+            "";
+
+        String[] DATA = {
+            // Column 1: characters to add to buffer (as if typed)
+            // Column 2: expected appearance of buffer after
+            //           keyboard xliteration.
+            "a", "a",
+            "b", "ab",
+            "t", "aby",
+            "c", "abyc",
+            "t", "abycy",
+            "h", "abycz",
+            null, "abycz", // null means finishKeyboardTransliteration
+        };
+
+        Transliterator t = new RuleBasedTransliterator("<ID>", RULES);
+        keyboardAux(t, DATA);
+    }
+
+    private void keyboardAux(Transliterator t, String[] DATA) {
+        Transliterator.Position index = new Transliterator.Position();
+        ReplaceableString s = new ReplaceableString();
+        for (int i=0; i<DATA.length; i+=2) {
+            StringBuffer log;
+            if (DATA[i] != null) {
+                log = new StringBuffer(s.toString() + " + "
+                                       + DATA[i]
+                                       + " -> ");
+                t.transliterate(s, index, DATA[i]);
+            } else {
+                log = new StringBuffer(s.toString() + " => ");
+                t.finishTransliteration(s, index);
+            }
+            String str = s.toString();
+            // Show the start index '{' and the cursor '|'
+            log.append(str.substring(0, index.start)).
+                append('{').
+                append(str.substring(index.start,
+                                     index.cursor)).
+                append('|').
+                append(str.substring(index.cursor));
+            if (str.equals(DATA[i+1])) {
+                logln(log.toString());
+            } else {
+                errln("FAIL: " + log.toString() + ", expected " + DATA[i+1]);
+            }
+        }
+    }
+
+    public void TestArabic() {
+        String DATA[] = {
+            "Arabic",
+                "\u062a\u062a\u0645\u062a\u0639 "+
+                "\u0627\u0644\u0644\u063a\u0629 "+
+                "\u0627\u0644\u0639\u0631\u0628\u0628\u064a\u0629 "+
+                "\u0628\u0628\u0646\u0638\u0645 "+
+                "\u0643\u062a\u0627\u0628\u0628\u064a\u0629 "+
+                "\u062c\u0645\u064a\u0644\u0629"
+        };
+
+        Transliterator t = Transliterator.getInstance("Latin-Arabic");
+        for (int i=0; i<DATA.length; i+=2) {
+            expect(t, DATA[i], DATA[i+1]);
+        }
+    }
+
+    /**
+     * Compose the Kana transliterator forward and reverse and try
+     * some strings that should come out unchanged.
+     */
+    public void TestCompoundKana() {
+        Transliterator t = new CompoundTransliterator("Latin-Kana;Kana-Latin");
+        expect(t, "aaaaa", "aaaaa");
+    }
+
+    /**
+     * Compose the hex transliterators forward and reverse.
+     */
+    public void TestCompoundHex() {
+        Transliterator a = Transliterator.getInstance("Unicode-Hex");
+        Transliterator b = Transliterator.getInstance("Hex-Unicode");
+        Transliterator[] trans = { a, b };
+        Transliterator ab = new CompoundTransliterator(trans);
+
+        // Do some basic tests of b
+        expect(b, "\\u0030\\u0031", "01");
+
+        String s = "abcde";
+        expect(ab, s, s);
+
+        trans = new Transliterator[] { b, a };
+        Transliterator ba = new CompoundTransliterator(trans);
+        ReplaceableString str = new ReplaceableString(s);
+        a.transliterate(str);
+        expect(ba, str.toString(), str.toString());
+    }
+
+    /**
+     * Do some basic tests of filtering.
+     */
+    public void TestFiltering() {
+        Transliterator hex = Transliterator.getInstance("Unicode-Hex");
+        hex.setFilter(new UnicodeFilter() {
+            public boolean contains(char c) {
+                return c != 'c';
+            }
+        });
+        String s = "abcde";
+        String out = hex.transliterate(s);
+        String exp = "\\u0061\\u0062c\\u0064\\u0065";
+        if (out.equals(exp)) {
+            logln("Ok:   \"" + exp + "\"");
+        } else {
+            logln("FAIL: \"" + out + "\", wanted \"" + exp + "\"");
+        }
+    }
+
+    /**
+     * Test pattern quoting and escape mechanisms.
+     */
+    public void TestPatternQuoting() {
+        // Array of 3n items
+        // Each item is <rules>, <input>, <expected output>
+        String[] DATA = {
+            "\u4E01>'[male adult]'", "\u4E01", "[male adult]",
+        };
+
+        for (int i=0; i<DATA.length; i+=3) {
+            logln("Pattern: " + Utility.escape(DATA[i]));
+            Transliterator t = new RuleBasedTransliterator("<ID>", DATA[i]);
+            expect(t, DATA[i+1], DATA[i+2]);
+        }
+    }
+
+    //======================================================================
+    // Support methods
+    //======================================================================
+
+    void expect(String rules, String source, String expectedResult) {
+        expect(new RuleBasedTransliterator("<ID>", rules), source, expectedResult);
+    }
+
+    void expect(Transliterator t, String source, String expectedResult,
+                Transliterator reverseTransliterator) {
+        expect(t, source, expectedResult);
+        if (reverseTransliterator != null) {
+            expect(reverseTransliterator, expectedResult, source);
+        }
+    }
+
+    void expect(Transliterator t, String source, String expectedResult) {
+        String result = t.transliterate(source);
+        expectAux(t.getID() + ":String", source, result, expectedResult);
+
+        ReplaceableString rsource = new ReplaceableString(source);
+        t.transliterate(rsource);
+        result = rsource.toString();
+        expectAux(t.getID() + ":Replaceable", source, result, expectedResult);
+
+        // Test keyboard (incremental) transliteration -- this result
+        // must be the same after we finalize (see below).
+        rsource.getStringBuffer().setLength(0);
+        Transliterator.Position index = new Transliterator.Position();
+        StringBuffer log = new StringBuffer();
+
+        for (int i=0; i<source.length(); ++i) {
+            if (i != 0) {
+                log.append(" + ");
+            }
+            log.append(source.charAt(i)).append(" -> ");
+            t.transliterate(rsource, index,
+                            String.valueOf(source.charAt(i)));
+            // Append the string buffer with a vertical bar '|' where
+            // the committed index is.
+            String s = rsource.toString();
+            log.append(s.substring(0, index.cursor)).
+                append('|').
+                append(s.substring(index.cursor));
+        }
+        
+        // As a final step in keyboard transliteration, we must call
+        // transliterate to finish off any pending partial matches that
+        // were waiting for more input.
+        t.finishTransliteration(rsource, index);
+        result = rsource.toString();
+        log.append(" => ").append(rsource.toString());
+
+        expectAux(t.getID() + ":Keyboard", log.toString(),
+                  result.equals(expectedResult),
+                  expectedResult);
+    }
+
+    void expectAux(String tag, String source,
+                   String result, String expectedResult) {
+        expectAux(tag, source + " -> " + result,
+                  result.equals(expectedResult),
+                  expectedResult);
+    }
+    
+    void expectAux(String tag, String summary, boolean pass,
+                   String expectedResult) {
+        if (pass) {
+            logln("("+tag+") " + Utility.escape(summary));
+        } else {
+            errln("FAIL: ("+tag+") "
+                  + Utility.escape(summary)
+                  + ", expected " + Utility.escape(expectedResult));
+        }
+    }
+}
diff --git a/src/com/ibm/test/translit/UnicodeSetTest.java b/src/com/ibm/test/translit/UnicodeSetTest.java
new file mode 100755
index 0000000..e3de87b
--- /dev/null
+++ b/src/com/ibm/test/translit/UnicodeSetTest.java
@@ -0,0 +1,170 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/test/translit/Attic/UnicodeSetTest.java,v $ 
+ * $Date: 2000/03/10 03:47:47 $ 
+ * $Revision: 1.6 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.test.translit;
+import com.ibm.text.*;
+import com.ibm.test.*;
+import java.text.*;
+import java.util.*;
+
+/**
+ * @test
+ * @summary General test of UnicodeSet
+ */
+public class UnicodeSetTest extends TestFmwk {
+
+    public static void main(String[] args) throws Exception {
+        new UnicodeSetTest().run(args);
+    }
+
+    public void TestPatterns() {
+        UnicodeSet set = new UnicodeSet();
+        expectPattern(set, "[[a-m]&[d-z]&[k-y]]",  "km");
+        expectPattern(set, "[[a-z]-[m-y]-[d-r]]",  "aczz");
+        expectPattern(set, "[a\\-z]",  "--aazz");
+        expectPattern(set, "[-az]",  "--aazz");
+        expectPattern(set, "[az-]",  "--aazz");
+        expectPattern(set, "[[[a-z]-[aeiou]i]]", "bdfnptvz");
+
+        // Throw in a test of complement
+        set.complement();
+        String exp = '\u0000' + "aeeoouu" + (char)('z'+1) + '\uFFFF';
+        expectPairs(set, exp);
+    }
+
+    public void TestCategories() {
+        UnicodeSet set = new UnicodeSet("[:Lu:]");
+        expectContainment(set, "ABC", "abc");
+    }
+
+    public void TestAddRemove() {
+        UnicodeSet set = new UnicodeSet();
+        set.add('a', 'z');
+        expectPairs(set, "az");
+        set.remove('m', 'p');
+        expectPairs(set, "alqz");
+        set.remove('e', 'g');
+        expectPairs(set, "adhlqz");
+        set.remove('d', 'i');
+        expectPairs(set, "acjlqz");
+        set.remove('c', 'r');
+        expectPairs(set, "absz");
+        set.add('f', 'q');
+        expectPairs(set, "abfqsz");
+        set.remove('a', 'g');
+        expectPairs(set, "hqsz");
+        set.remove('a', 'z');
+        expectPairs(set, "");
+
+        // Try removing an entire set from another set
+        expectPattern(set, "[c-x]", "cx");
+        UnicodeSet set2 = new UnicodeSet();
+        expectPattern(set2, "[f-ky-za-bc[vw]]", "acfkvwyz");
+        set.removeAll(set2);
+        expectPairs(set, "deluxx");
+
+        // Try adding an entire set to another set
+        expectPattern(set, "[jackiemclean]", "aacceein");
+        expectPattern(set2, "[hitoshinamekatajamesanderson]", "aadehkmort");
+        set.addAll(set2);
+        expectPairs(set, "aacehort");
+
+        // Test commutativity
+        expectPattern(set, "[hitoshinamekatajamesanderson]", "aadehkmort");
+        expectPattern(set2, "[jackiemclean]", "aacceein");
+        set.addAll(set2);
+        expectPairs(set, "aacehort");
+    }
+
+    void expectContainment(UnicodeSet set, String charsIn, String charsOut) {
+        StringBuffer bad = new StringBuffer();
+        if (charsIn != null) {
+            for (int i=0; i<charsIn.length(); ++i) {
+                char c = charsIn.charAt(i);
+                if (!set.contains(c)) {
+                    bad.append(c);
+                }
+            }
+            if (bad.length() > 0) {
+                logln("Fail: set " + set + " does not contain " + bad +
+                      ", expected containment of " + charsIn);
+            } else {
+                logln("Ok: set " + set + " contains " + charsIn);
+            }
+        }
+        if (charsOut != null) {
+            bad.setLength(0);
+            for (int i=0; i<charsOut.length(); ++i) {
+                char c = charsOut.charAt(i);
+                if (set.contains(c)) {
+                    bad.append(c);
+                }
+            }
+            if (bad.length() > 0) {
+                logln("Fail: set " + set + " contains " + bad +
+                      ", expected non-containment of " + charsOut);
+            } else {
+                logln("Ok: set " + set + " does not contain " + charsOut);
+            }
+        }
+    }
+
+    void expectPattern(UnicodeSet set,
+                       String pattern,
+                       String expectedPairs) {
+        set.applyPattern(pattern);
+        if (!set.getPairs().equals(expectedPairs)) {
+            errln("FAIL: applyPattern(\"" + pattern +
+                  "\") => pairs \"" +
+                  escape(set.getPairs()) + "\", expected \"" +
+                  escape(expectedPairs) + "\"");
+        } else {
+            logln("Ok:   applyPattern(\"" + pattern +
+                  "\") => pairs \"" +
+                  escape(set.getPairs()) + "\"");
+        }
+    }
+
+    void expectPairs(UnicodeSet set, String expectedPairs) {
+        if (!set.getPairs().equals(expectedPairs)) {
+            errln("FAIL: Expected pair list \"" +
+                  escape(expectedPairs) + "\", got \"" +
+                  escape(set.getPairs()) + "\"");
+        }
+    }
+
+    /**
+     * Escape non-ASCII characters as Unicode.
+     */
+    static final String escape(String s) {
+        StringBuffer buf = new StringBuffer();
+        for (int i=0; i<s.length(); ++i) {
+            char c = s.charAt(i);
+            if (c >= ' ' && c <= 0x007F) {
+                buf.append(c);
+            } else {
+                buf.append("\\u");
+                if (c < 0x1000) {
+                    buf.append('0');
+                    if (c < 0x100) {
+                        buf.append('0');
+                        if (c < 0x10) {
+                            buf.append('0');
+                        }
+                    }
+                }
+                buf.append(Integer.toHexString(c));
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/src/com/ibm/text/BreakDictionary.java b/src/com/ibm/text/BreakDictionary.java
new file mode 100755
index 0000000..1656027
--- /dev/null
+++ b/src/com/ibm/text/BreakDictionary.java
@@ -0,0 +1,296 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/text/Attic/BreakDictionary.java,v $ 
+ * $Date: 2000/03/10 04:07:18 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+package com.ibm.text;
+
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.FileOutputStream;
+
+import com.ibm.util.CompactByteArray;
+
+/**
+ * This is the class that represents the list of known words used by
+ * DictionaryBasedBreakIterator.  The conceptual data structure used
+ * here is a trie: there is a node hanging off the root node for every
+ * letter that can start a word.  Each of these nodes has a node hanging
+ * off of it for every letter that can be the second letter of a word
+ * if this node is the first letter, and so on.  The trie is represented
+ * as a two-dimensional array that can be treated as a table of state
+ * transitions.  Indexes are used to compress this array, taking
+ * advantage of the fact that this array will always be very sparse.
+ */
+public class BreakDictionary {
+    //=================================================================================
+    // testing and debugging
+    //=================================================================================
+    public static void main(String args[])
+            throws FileNotFoundException, UnsupportedEncodingException, IOException {
+        String filename = args[0];
+
+        BreakDictionary dictionary = new BreakDictionary(new FileInputStream(filename));
+
+        String outputFile = "";
+        if(args.length >= 2) {
+            outputFile = args[1];
+        }
+        PrintWriter out = null;
+        if (outputFile.length() != 0) {
+            out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UnicodeLittle"));
+        }
+        dictionary.printWordList("", 0, out);
+    }
+
+    public void printWordList(String partialWord, int state, PrintWriter out)
+            throws IOException {
+        if (state == -1) {
+            System.out.println(partialWord);
+            if (out != null) {
+                out.println(partialWord);
+            }
+        }
+        else {
+            for (int i = 0; i < numCols; i++) {
+                if (at(state, i) != 0) {
+                    printWordList(partialWord + reverseColumnMap[i], at(state, i), out);
+                }
+            }
+        }
+    }
+
+    /**
+     * A map used to go from column numbers to characters.  Used only
+     * for debugging right now.
+     */
+    private char[] reverseColumnMap = null;
+
+    //=================================================================================
+    // data members
+    //=============================================