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=""<font size=-1>Copyright (c); 1998-2000 IBM Corporation.</font>""
+ 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’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
+("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
+RECIPIENT’S ACCEPTANCE OF THIS AGREEMENT.</p>
+<b>
+
+<p>1. DEFINITIONS</p>
+</b>
+
+<p>"Contribution" means: </p>
+
+<blockquote>
+ <blockquote>
+ <p>a) in the case of International Business Machines Corporation ("IBM"), 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 ‘originates’ from a Contributor
+ if it was added to the Program by such Contributor itself or anyone acting on such
+ Contributor’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>"Contributor" means IBM and any other entity that distributes the Program.</p>
+
+<p>"Licensed Patents " 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>"Original Program" 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>"Program" means the Original Program and Contributions.</p>
+
+<p>"Recipient" 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’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 ("Commercial Contributor") hereby
+agrees to defend and indemnify every other Contributor ("Indemnified
+Contributor") against any losses, damages and costs (collectively "Losses")
+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’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 "AS
+IS" 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’s patent(s), then such
+Recipient’s rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed. </p>
+
+<p>All Recipient’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’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 "
+ + ""lightweight" (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
+ //=============================================