This commit was manufactured by cvs2svn to create tag
'release-1-3-2'.

X-SVN-Rev: 4717
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..5013448
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,99 @@
+* 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/data/misc/english.dict -text
+src/com/ibm/text/resources/Transliterator_Bengali_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Devanagari_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Fullwidth_Halfwidth.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Gujarati_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Gurmukhi_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Han_Pinyin.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Hiragana_Katakana.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Bengali.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Devanagari.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Gujarati.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Gurmukhi.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Kannada.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Malayalam.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Oriya.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Tamil.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_InterIndic_Telugu.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Kanji_English.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Kanji_OnRomaji.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Kannada_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_KeyboardEscape_Latin1.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Arabic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Cyrillic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Devanagari.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Greek.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Hebrew.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Jamo.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Latin_Kana.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Malayalam_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Oriya_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_StraightQuotes_CurlyQuotes.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Tamil_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_Telugu_InterIndic.utf8.txt -text
+src/com/ibm/text/resources/Transliterator_UnicodeName_UnicodeChar.utf8.txt -text
+src/com/ibm/text/resources/thai_dict -text
+src/com/ibm/text/unames.dat -text
+src/com/ibm/text/uprops.dat -text
+src/data/holidays_jp.ucs -text
+src/data/thai6.ucs -text
+src/data/unicode/Draft-TestSuite.txt -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/buildall.bat b/buildall.bat
new file mode 100755
index 0000000..40e098f
--- /dev/null
+++ b/buildall.bat
@@ -0,0 +1,2 @@
+javac -sourcepath src -d classes -classpath classes src/com/ibm/math/*.java src/com/ibm/util/*.java src/com/ibm/util/resources/*.java src/com/ibm/text/*.java src/com/ibm/text/resources/*.java src/com/ibm/text/components/*.java src/com/ibm/test/*.java src/com/ibm/test/bnf/*.java src/com/ibm/test/calendar/*.java src/com/ibm/test/compression/*.java src/com/ibm/test/normalizer/*.java src/com/ibm/test/rbbi/*.java src/com/ibm/test/rbnf/*.java src/com/ibm/test/search/*.java src/com/ibm/test/topleveltest/*.java src/com/ibm/test/translit/*.java src/com/ibm/demo/*.java src/com/ibm/demo/calendar/*.java src/com/ibm/demo/holiday/*.java src/com/ibm/demo/rbbi/*.java src/com/ibm/demo/rbnf/*.java src/com/ibm/demo/translit/*.java src/com/ibm/tools/compression/*.java src/com/ibm/tools/normalizer/*.java src/com/ibm/tools/RuleBasedBreakIterator/*.java
+javadoc -sourcepath src -d docs -classpath classes src/com/ibm/math/*.java src/com/ibm/util/*.java src/com/ibm/util/resources/*.java src/com/ibm/text/*.java src/com/ibm/text/resources/*.java src/com/ibm/text/components/*.java src/com/ibm/test/*.java src/com/ibm/test/bnf/*.java src/com/ibm/test/calendar/*.java src/com/ibm/test/compression/*.java src/com/ibm/test/normalizer/*.java src/com/ibm/test/rbbi/*.java src/com/ibm/test/rbnf/*.java src/com/ibm/test/search/*.java src/com/ibm/test/topleveltest/*.java src/com/ibm/test/translit/*.java src/com/ibm/demo/*.java src/com/ibm/demo/calendar/*.java src/com/ibm/demo/holiday/*.java src/com/ibm/demo/rbbi/*.java src/com/ibm/demo/rbnf/*.java src/com/ibm/demo/translit/*.java src/com/ibm/tools/compression/*.java src/com/ibm/tools/normalizer/*.java src/com/ibm/tools/RuleBasedBreakIterator/*.java
diff --git a/src/com/ibm/data/misc/english.dict b/src/com/ibm/data/misc/english.dict
new file mode 100755
index 0000000..10f430b
--- /dev/null
+++ b/src/com/ibm/data/misc/english.dict
Binary files differ
diff --git a/src/com/ibm/data/misc/words.txt b/src/com/ibm/data/misc/words.txt
new file mode 100755
index 0000000..a7e06ac
--- /dev/null
+++ b/src/com/ibm/data/misc/words.txt
@@ -0,0 +1,2990 @@
+--
+--such
+a
+a'bel-mizraim
+a'iah
+abate
+abated
+abdicated
+abel
+abi'da
+abide
+abim'a-el
+abim'elech
+abim'elech's
+able
+abolish
+abolishing
+abomination
+about
+above
+abraham
+abraham's
+abram
+abram's
+abroad
+absent
+absolute
+absolved
+abundance
+abundantly
+abuses
+accad
+accept
+accepted
+accommodation
+according
+accordingly
+account
+accustomed
+achbor
+acknowledged
+acquiesce
+acquired
+across
+act
+acts
+adah
+adam
+adbeel
+add
+added
+admah
+administration
+adullamite
+advanced
+adviser
+afar
+affected
+afflicted
+affliction
+afraid
+after
+afterward
+afterwards
+again
+against
+age
+ages
+agile
+agree
+agreed
+ahuz'zath
+ai
+aid
+air
+akan
+al'lon-bacuth
+alighted
+alive
+all
+allegiance
+alliances
+allies
+allow
+allowance
+almighty
+almo'dad
+almond
+almonds
+alone
+along
+aloud
+already
+also
+altar
+alter
+altering
+although
+altogether
+alvah
+alvan
+am
+am'alek
+am'raphel
+amal'ekites
+amazement
+america
+ammonites
+among
+amongst
+amorite
+amorites
+amount
+an
+an'amim
+anah
+and
+aner
+angel
+angels
+anger
+angry
+animal
+animals
+annihilation
+anointed
+another
+another's
+answer
+answered
+any
+anything
+anywhere
+apart
+appealed
+appealing
+appear
+appeared
+appease
+appoint
+appointed
+approached
+approaching
+appropriations
+aprons
+ar'arat
+ar'ioch
+ar'vadites
+aram
+aramean
+aran
+arbitrary
+archers
+ard
+are
+are'li
+area
+arise
+ark
+arkites
+armed
+armies
+arms
+army
+aro'di
+arose
+around
+arpach'shad
+arrayed
+arrive
+art
+as
+as'enath
+ascending
+ash'kenaz
+ash'teroth-karna'im
+ashamed
+ashbel
+asher
+asher's
+ashes
+aside
+ask
+asked
+asks
+asleep
+ass
+ass's
+assemble
+assembled
+assent
+asses
+asshu'rim
+asshur
+assume
+assured
+assyria
+at
+atad
+ate
+attack
+attacked
+attained
+attempts
+attend
+attended
+attention
+authority
+avenged
+avith
+away
+awesome
+awoke
+ba'al-ha'nan
+ba'bel
+back
+backward
+bad
+bade
+bags
+baked
+baker
+balm
+bank
+banks
+barbarous
+barren
+bas'emath
+basket
+baskets
+battle
+bdellium
+be
+be'or
+be-e'ri
+bear
+bearing
+bears
+beast
+beasts
+beautiful
+became
+because
+becher
+become
+becomes
+bed
+bedad
+been
+beer-la'hai-roi
+beer-sheba
+befall
+befallen
+befalls
+before
+beg
+began
+beginning
+beguiled
+begun
+beheld
+behind
+behold
+being
+bela
+believe
+believed
+belly
+belong
+belonging
+belongs
+below
+ben-ammi
+ben-o'ni
+beneath
+benefits
+benjamin
+benjamin's
+bera
+bereaved
+bered
+bereft
+beri'ah
+beside
+besides
+besought
+best
+bethel
+bethlehem
+bethu'el
+better
+between
+beyond
+bilhah
+bilhan
+binding
+bird
+birds
+birsha
+birth
+birthday
+birthright
+bites
+bitter
+bitumen
+black
+blame
+blameless
+bless
+blessed
+blesses
+blessing
+blessings
+blighted
+blindness
+blood
+blossoms
+blot
+blotted
+blow
+bodies
+body
+boiling
+bonds
+bone
+bones
+book
+booths
+border
+bore
+born
+borne
+both
+bough
+bought
+bound
+boundaries
+bounties
+bow
+bowed
+bowing
+bowshot
+boys
+bozrah
+bracelets
+branches
+breach
+bread
+breadth
+break
+breaking
+breasts
+breath
+breathed
+bred
+breed
+breeding
+brethren
+brick
+bricks
+brimstone
+bring
+britain
+british
+broken
+bronze
+brother
+brother's
+brother-in-law
+brothers
+brought
+brow
+bruise
+budded
+build
+building
+built
+bulls
+bundle
+bundles
+buried
+burn
+burned
+burnt
+burst
+bury
+burying
+bushes
+but
+butler
+butlership
+buy
+buz
+by
+cain
+cake
+cakes
+calah
+calf
+call
+called
+calls
+came
+camel
+camel's
+camels
+camp
+camped
+can
+canaan
+canaan'
+canaanite
+canaanites
+canaanitish
+candid
+cannot
+caph'torim
+captain
+captive
+captives
+captured
+caravan
+carcasses
+care
+carefully
+carmi
+carried
+carry
+cases
+caslu'him
+cast
+cattle
+caught
+cause
+caused
+causes
+cave
+cease
+ceased
+certain
+chain
+chalde'ans
+chamber
+change
+changed
+character
+charge
+charged
+charging
+chariot
+chariots
+charters
+cheat
+cheated
+ched-or-lao'mer
+cheran
+cherubim
+chesed
+chezib
+chief
+chiefs
+child
+childbearing
+childless
+children
+children's
+choice
+choicest
+chose
+chosen
+circumcised
+circumstances
+cities
+citizens
+city
+civil
+civilized
+clans
+clean
+clear
+cleaves
+close
+closed
+clothed
+clothes
+clothing
+cloud
+clouds
+clusters
+coastland
+coasts
+coffin
+cold
+colonies
+colonies:
+colt
+colts
+combined
+come
+comely
+comes
+comfort
+comforted
+comforts
+coming
+command
+commanded
+commander
+commandments
+commerce
+commit
+committed
+common
+companies
+company
+complained
+complete
+completed
+compliance
+conceal
+conceived
+concern
+concerning
+conclude
+concubine
+concubines
+condition
+conditions
+confined
+confuse
+confused
+congress
+conjured
+connected
+connection
+connections
+consent
+consent:
+conspired
+constitution
+constrained
+constrains
+consume
+consumed
+contempt
+contended
+continually
+continue
+continued
+contract
+control
+controlling
+convulsions
+cool
+cord
+correspondence
+corrupt
+corrupted
+costly
+couch
+couched
+couches
+couching
+could
+council
+count
+counted
+countenance
+country
+course
+covenant
+cover
+covered
+covering
+cows
+created
+creation
+creator
+creature
+creatures
+creeping
+creeps
+cried
+crossed
+crossing
+crouching
+crown
+cruel
+cruelty
+cry
+crying
+cubit
+cubits
+cup
+curds
+current
+curse
+cursed
+curses
+cush
+custody
+cut
+cutting
+dainties
+damascus
+dan
+dangers
+dares
+dark
+darkness
+daughter
+daughter-in-law
+daughters
+dawned
+day
+days
+dead
+deal
+dealt
+death
+deb'orah
+deceitfully
+deceived
+decent
+decide
+decks
+declaration
+declare
+declaring
+dedan
+deed
+deep
+defeat
+defeated
+defiled
+define
+delay
+delayed
+delight
+deliver
+delivered
+delivery
+denied
+denounces
+depart
+departed
+departing
+dependent
+dependents
+depository
+depriving
+deriving
+descendants
+descending
+design
+desire
+desired
+desolate
+desolation
+despised
+despotism
+destroy
+destroyed
+destroys
+destruction
+destructive
+determined
+devoured
+devouring
+dew
+dictate
+did
+didst
+die
+died
+dies
+diklah
+dim
+din'habah
+dinah
+dinah's
+dine
+dipped
+direct
+direction
+disaster
+disavow
+discreet
+disgrace
+dishan
+dishon
+dismayed
+displeased
+displeasing
+disposed
+dissolutions
+dissolve
+dissolved
+distance
+distant
+distress
+distressed
+districts
+divide
+divided
+dividing
+divination
+divine
+divines
+do
+do'danim
+doer
+does
+doing
+domestic
+dominion
+done
+door
+dothan
+double
+doubling
+doubt
+dove
+down
+downcast
+dowry
+drank
+draw
+drawn
+dread
+dream
+dreamed
+dreamer
+dreams
+drew
+dried
+drink
+drinking
+drinks
+driven
+drove
+droves
+drunk
+dry
+dug
+dumah
+dungeon
+during
+dust
+duty
+dwell
+dwelling
+dwelt
+e'domites
+e'phraim
+e'phraim's
+each
+early
+ears
+earth
+easily
+east
+eastward
+eat
+eaten
+eating
+ebal
+eber
+eden
+eder
+edom
+effect
+egypt
+egyptian
+egyptian's
+egyptians
+ehi
+eight
+eighteen
+eighty
+eighty-seven
+eighty-six
+eighty-two
+either
+el'iphaz
+el-bethel
+el-el'ohe-israel
+el-paran
+elah
+elam
+elda'ah
+elder
+elders
+eldest
+elected
+eleven
+eli'shah
+elie'zer
+ella'sar
+elon
+else
+embalm
+embalmed
+embalming
+embrace
+embraced
+emigration
+emim
+emptied
+empty
+empty-handed
+enaim
+encamped
+encampments
+encourage
+end
+endeavored
+ended
+ending
+endowed
+ends
+enemies
+enemy
+english
+enlarge
+enlarging
+enmish'pat
+enmity
+enoch
+enosh
+enough
+enter
+entered
+entitle
+entrance
+entreat
+envied
+ephah
+epher
+ephrath
+ephron
+equal
+er
+erech
+erected
+eri
+errand
+esau
+esau's
+escape
+escaped
+esek
+eshban
+eshcol
+establish
+established
+establishing
+establishment
+eternal
+euphra'tes
+euphrates
+eve
+even
+evening
+events
+ever
+everlasting
+every
+everything
+everywhere
+evil
+evils
+evinces
+ewe
+ewes
+example
+exceedingly
+except
+exchange
+excited
+executioners
+exercise
+experience
+expert
+explain
+exposed
+exposing
+extend
+extended
+eyes
+ezbon
+ezer
+face
+faces
+facts
+failed
+fainted
+fair
+faithfulness
+fall
+fallen
+falls
+falsely
+families
+family
+famine
+famished
+far
+fare
+fared
+farewell
+fast
+fat
+father
+father's
+father-in-law
+fathers
+fatiguing
+fatness
+faults
+favor
+favorable
+fawns
+fear
+feared
+feast
+fed
+feebler
+feed
+feel
+feet
+fell
+fellow
+felt
+female
+festal
+fetch
+few
+field
+fields
+fierce
+fiercely
+fifteen
+fifth
+fifths
+fifty
+fig
+fill
+filled
+find
+finds
+fine
+finish
+finished
+fire
+firm
+firmament
+firmness
+first
+first-
+first-born
+firstlings
+fish
+fit
+five
+fixed
+flaming
+fled
+flee
+fleeing
+flesh
+floated
+flock
+flocks
+flood
+floor
+flowed
+flows
+fly
+foal
+follow
+followed
+following
+follows
+folly
+fondling
+food
+foolishly
+foot
+for
+forbidden
+force
+forced
+forces
+ford
+foreign
+foreigner
+foreigners
+foremost
+foreskin
+foreskins
+forger
+forget
+forgets
+forgive
+forgot
+forgotten
+form
+formed
+former
+formerly
+formidable
+forms
+forsaken
+forth
+fortune
+fortunes
+forty
+forty-five
+forty-seven
+found
+foundation
+fountains
+four
+fourteen
+fourteenth
+fourth
+frail
+free
+freely
+fresh
+freshly
+friend
+friendly
+friends
+fro
+from
+front
+frontiers
+fruit
+fruitful
+fruits
+fugitive
+fulfil
+fulfilled
+full
+fundamentally
+furnace
+fury
+future
+gad
+gaham
+gained
+galeed
+game
+garden
+garment
+garments
+gatam
+gate
+gather
+gathered
+gaunt
+gave
+gavest
+gaza
+gazed
+genealogies
+general
+generation
+generations
+gera
+gerar
+gershon
+get
+gether
+gift
+gifts
+gihon
+gilead
+gir'gashites
+give
+given
+givest
+giving
+go
+goat
+goats
+god
+god's
+gods
+goes
+goi'im
+going
+gold
+gomer
+gomor'rah
+gone
+good
+good-looking
+goods
+gopher
+goshen
+got
+gotten
+governed
+government
+governments
+governments:
+governor
+governors
+gracious
+graciously
+grain
+grandchildren
+grandson
+grant
+granted
+grapes
+grass
+grave
+gray
+great
+greater
+greatly
+green
+grew
+grieved
+grievous
+groping
+ground
+grow
+growing
+grown
+grows
+guard
+guards
+guile
+guilt
+guilty
+gum
+guni
+ha'mathites
+had
+hadad
+hadar
+hador'am
+hagar
+haggi
+hairs
+hairy
+half
+hallowed
+ham
+hamor
+hamor's
+hamstring
+hamul
+hand
+hands
+handsome
+hang
+hanged
+hanoch
+happened
+happiness
+happy
+haran
+harass
+harassed
+hard
+hardship
+harlot
+harlotry
+harm
+harshly
+harvest
+harvests
+has
+hast
+haste
+hastened
+hastily
+hate
+hated
+hath
+hav'ilah
+have
+haven
+havilah
+having
+haz'azon-ta'mar
+hazarma'veth
+hazo
+he
+he-asses
+he-goats
+head
+heads
+healed
+heap
+hear
+heard
+hearing
+hearken
+hearkened
+hears
+heart
+hearts
+heat
+heaven
+heavens
+heber
+hebrew
+hebrews
+hebron
+heed
+heeded
+heel
+heels
+heifer
+height
+heir
+held
+help
+helper
+heman
+hemdan
+her
+herb
+herd
+herds
+herdsmen
+here
+herself
+heth
+hewed
+hezron
+hid
+hidden
+hide
+high
+hill
+hills
+him
+himself
+hind
+hinder
+hip
+hirah
+hire
+hired
+his
+history
+hither
+hittite
+hittites
+hivite
+hivites
+hobah
+hold
+hollow
+home
+honest
+honesty
+honey
+honor
+honored
+hori
+horite
+horites
+horns
+horse's
+horsemen
+horses
+host
+hot
+hotly
+house
+household
+households
+houses
+how
+hul
+human
+humane
+humble
+humbled
+hundred
+hundredfold
+hundredth
+hunt
+hunted
+hunter
+hunting
+huppim
+husband
+husham
+hushim
+i
+if
+ill
+ill-treat
+image
+imagination
+immediate
+imnah
+impel
+importance
+imposing
+impossible
+in
+inasmuch
+incapable
+including
+increased
+indeed
+independent
+indian
+indignant
+inestimable
+inevitably
+inhabitants
+inheritance
+iniquity
+injuries
+injury
+innocence
+innocent
+inquire
+inquired
+inside
+instead
+institute
+instituted
+instructed
+instructing
+instrument
+instruments
+insult
+insurrections
+integrity
+intended
+intentions
+interpret
+interpretation
+interpretations
+interpreted
+interpreter
+interrupt
+into
+introducing
+invariably
+invasion
+invasions
+invested
+iob
+irad
+iram
+iron
+is
+is'sachar
+isaac
+isaac's
+iscah
+ish'mael
+ish'mael's
+ish'maelites
+ishbak
+ishvah
+ishvi
+israel
+israel's
+israelites
+it
+ithran
+its
+itself
+jabal
+jabbok
+jachin
+jacob
+jacob's
+jah'leel
+jahzeel
+jalam
+jamin
+japheth
+jar
+jared
+javan
+je'gar-sahadu'tha
+je'ush
+jealous
+jeb'usites
+jemu'el
+jerah
+jesting
+jetheth
+jetur
+jewelry
+jezer
+jidlaph
+jobab
+joined
+joint
+jokshan
+joktan
+jordan
+joseph
+joseph's
+journey
+journeyed
+jubal
+judah
+judah's
+judge
+judged
+judges
+judgment
+judiciary
+judith
+jurisdiction
+jury:
+just
+justice
+kad'monites
+kadesh
+ked'emah
+kedar
+keep
+keeper
+keepers
+kemu'el
+ken'ites
+ken'izzites
+kenan
+kenaz
+kept
+ketu'rah
+kid
+kids
+kill
+killed
+kind
+kindled
+kindness
+kindred
+kinds
+king
+king's
+kingdom
+kings
+kinsman
+kinsmen
+kir'iath-ar'ba
+kiss
+kissed
+kittim
+knead
+knee
+kneel
+knees
+knew
+knife
+know
+knowing
+knowledge
+known
+knows
+kohath
+korah
+laban
+laban's
+labor
+lack
+lacking
+lad
+lad's
+ladder
+lads
+laid
+lain
+lamb
+lambs
+lamech
+lamentation
+lamented
+land
+lands
+language
+languages
+languished
+large
+lasha
+last
+later
+laugh
+laughed
+laughter
+laws
+lay
+laying
+le-um'mim
+lead
+leaf
+leah
+leah's
+lean
+leap
+leaped
+learn
+learned
+least
+leave
+leaves
+led
+left
+legislate
+legislation:
+legislative
+legislature
+legislatures
+leha'bim
+length
+lentils
+lesser
+lest
+let
+letu'shim
+levey
+levi
+liberty
+lie
+lies
+life
+lifeblood
+lift
+lifted
+light
+lights
+like
+likely
+likeness
+likewise
+limping
+linen
+lingered
+lion
+lion's
+lioness
+listen
+listened
+listening
+little
+live
+lived
+lives
+livestock
+living
+lo
+load
+loaded
+lodge
+lodged
+lodging
+loins
+long
+longed
+longer
+longs
+look
+looked
+looking
+loose
+lord
+lord's
+lords
+loss
+lot
+lot's
+lotan
+lotan's
+loud
+love
+loved
+lovely
+loves
+lower
+lowered
+loyally
+lud
+ludim
+luz
+lying
+lyre
+ma'acah
+ma'halath
+ma-hal'alel
+mach-
+mach-pe'lah
+machir
+madai
+made
+mag'diel
+magicians
+magnanimity
+magog
+mahana'im
+maid
+maiden
+maids
+maidservants
+make
+maker
+making
+mal'chi-el
+male
+males
+mamre
+man
+man'ahath
+man's
+manas'seh
+manas'seh's
+mandrakes
+mankind
+manly
+manner
+mantle
+many
+mark
+marked
+marriage
+marriages
+married
+marries
+marry
+mash
+masre'kah
+massa
+master
+master's
+mate
+mating
+matred
+matter
+may
+me
+me'zahab
+me-hu'ja-el
+me-thu'sha-el
+meal
+mean
+meaning
+means
+meant
+meantime
+meanwhile
+measure
+measured
+measures
+medan
+meditate
+meet
+meets
+mehet'abel
+mel-chiz'edek
+members
+men
+men's
+mend
+mended
+menservants
+mention
+merar'i
+mercenaries
+merchants
+merciful
+merciless
+mercy
+merry
+mesha
+meshech
+mesopota'mia
+message
+messengers
+met
+methu'selah
+mibsam
+mibzar
+mid'ian
+mid'ianite
+mid'ianites
+midst
+midwife
+might
+mightier
+mightily
+mighty
+migrated
+migration
+milcah
+milch
+military
+milk
+mind
+mine
+mirth
+miscarried
+mishma
+mist
+mistress
+mizpah
+mizzah
+moab
+moabites
+mock
+mocking
+money
+monsters
+month
+months
+moon
+more
+moreh
+moreover
+mori'ah
+morning
+morsel
+mortar
+most
+mother
+mother's
+mothers
+mottled
+mount
+mountain
+mountains
+mourn
+mourned
+mourning
+mouth
+moved
+moves
+moving
+much
+multiplied
+multiply
+multitude
+muppim
+murders
+must
+mutually
+my
+myrrh
+myself
+na'amah
+na'aman
+nahath
+nahor
+nahor's
+naked
+nakedness
+name
+named
+names
+naph'tali
+naph-tu'him
+naphish
+nation
+nations
+native
+naturalization
+nature
+nature's
+near
+neba'ioth
+necessary
+necessity
+neck
+need
+negeb
+neglected
+neighboring
+neither
+nephilim
+never
+nevertheless
+new
+next
+night
+nights
+nile
+nimrod
+nin'eveh
+nine
+nineteen
+ninety
+ninety-five
+ninety-nine
+no
+noah
+noah's
+nod
+none
+noon
+nor
+north
+northward
+nose
+nostrils
+not
+nothing
+now
+number
+numbered
+numbers
+nurse
+nuts
+o
+oak
+oaks
+oath
+obal
+obedience
+obeisance
+obey
+obeyed
+object
+obstructed
+obstructing
+obtain
+obtained
+occasion
+occupation
+odious
+odor
+of
+off
+offended
+offense
+offenses:
+offer
+offered
+offering
+offerings
+office
+officer
+officers
+offices
+offspring
+oh
+ohad
+oholiba'mah
+oil
+old
+older
+oldest
+olive
+omar
+on
+onam
+onan
+once
+one
+ones
+only
+onyx
+open
+opened
+operation
+ophir
+opinions
+opposing
+opposite
+oppressed
+oppressions
+or
+order
+orders
+organizing
+ornaments
+other
+others
+ought
+our
+ours
+ourselves
+out
+outcry
+outside
+outwitted
+over
+overdriven
+overseer
+overseers
+oversight
+overtake
+overthrew
+overthrow
+overtook
+own
+owns
+oxen
+pace
+paddan
+paddan-aram
+paid
+pain
+pair
+pairs
+pallu
+paralleled
+paran
+part
+parts
+pass
+passed
+past
+pasture
+pastured
+pasturing
+path
+pathru'sim
+patient
+pau
+pay
+payment
+pe'lah
+peace
+peaceably
+peeled
+peleg
+peni'el
+penu'el
+people
+peopled
+peoples
+per'izzites
+perez
+perfidy
+perform
+perhaps
+perish
+permit
+perpetuated
+persons
+petitioned
+petitions
+pharaoh
+pharaoh's
+phicol
+philistines
+physicians
+piece
+pieces
+pigeon
+pildash
+pillar
+pinon
+pipe
+pishon
+pistachio
+pit
+pitch
+pitched
+pits
+place
+placed
+places
+plagues
+plain
+plainly
+plane
+planning
+plant
+planted
+plants
+play
+played
+playing
+pleasant
+please
+pleased
+pleases
+pleasing
+pleasure
+pledge
+plenteous
+plenty
+plowing
+plucked
+plump
+plundered
+point
+political
+poor
+poplar
+population
+portion
+portions
+possess
+possession
+possessions
+posterity
+pot
+pot'i-phar
+poti'phera
+pottage
+poured
+poverty
+power
+powers
+praise
+praised
+pray
+prayed
+prayer
+pre-eminence
+pre-eminent
+prepare
+prepared
+presence
+present
+presented
+preserve
+preserved
+pressed
+pressing
+pretended
+prevail
+prevailed
+prevent
+prevented
+prey
+price
+pride
+priest
+priests
+prince
+princes
+principles
+prison
+prisoners
+proceed
+produce
+profit
+promise
+promised
+pronounce
+property
+prophet
+proposal
+propose
+prosper
+prospered
+protecting
+protection
+prove
+provender
+provide
+provided
+providence
+province
+provision
+provisions
+prudence
+public
+publish
+punishment
+purchased
+purify
+purpose
+pursue
+pursued
+pursuing
+pursuit
+put
+putting
+puvah
+quarrel
+quarreled
+quartering
+questioned
+questions
+quickly
+quiet
+quiver
+ra'amah
+rachel
+rachel's
+raid
+raiders
+raiment
+rain
+rained
+raise
+raised
+raising
+ram
+ram'eses
+rams
+ran
+rather
+ravaged
+raven
+ravenous
+re'u
+reached
+ready
+really
+reaped
+reason
+reassured
+rebekah
+rebekah's
+rebelled
+rebuked
+receded
+receive
+received
+reckoned
+reckoning
+recognize
+recognized
+records
+rectitude
+red
+redeemed
+redress
+reduce
+reed
+refresh
+refused
+refusing
+regard
+regarded
+regards
+reho'both
+reho'both-ir
+reign
+reigned
+reliance
+relief
+relinquish
+remain
+remained
+remaining
+remains
+remember
+remembered
+reminded
+remnant
+remove
+removed
+removing
+render
+renown
+rent
+repeated
+repeatedly
+reph'aim
+replace
+replaced
+replied
+report
+representation
+representative
+representatives
+reproach
+require
+required
+requires
+rescue
+resen
+reserve
+reserved
+respect
+rest
+rested
+resting
+restore
+restored
+restrained
+return
+returned
+reu'el
+reuben
+reumah
+revealed
+revived
+reward
+rib
+ribs
+rich
+ride
+rider
+right
+righted
+righteous
+righteousness
+rightly
+rights
+ring
+rings
+ripened
+riphath
+rise
+risen
+river
+rivers
+road
+robe
+rock
+rode
+rods
+roll
+rolled
+roof
+room
+rooms
+rose
+rosh
+roughly
+round
+rouse
+routed
+royal
+rule
+ruler
+ruler's
+run
+runnels
+sab'teca
+sabtah
+sack
+sackcloth
+sacks
+sacred
+sacrifice
+sacrifices
+saddle
+saddled
+safely
+safety
+said
+sake
+salaries
+salem
+salt
+salvation
+same
+samlah
+sand
+sandal-thong
+sar'ai
+sarah
+sarah's
+sat
+savages
+saved
+saving
+savory
+saw
+say
+saying
+says
+scarcely
+scarlet
+scatter
+scattered
+scepter
+se'ir
+sea
+searched
+seas
+seashore
+season
+seasons
+seba
+second
+secretly
+secure
+security
+see
+seed
+seedtime
+seeing
+seek
+seeking
+seem
+seemed
+seen
+sees
+seize
+seized
+select
+self-evident
+sell
+semen
+send
+sent
+separate
+separated
+separation
+sephar
+sepulchre
+sepulchres
+serah
+sered
+serpent
+serug
+servant
+servant's
+servants
+serve
+served
+service
+serving
+set
+seth
+settle
+settled
+settlement
+seven
+sevenfold
+seventeen
+seventeenth
+seventh
+seventy
+seventy-five
+seventy-seven
+seventy-sevenfold
+severe
+sewed
+sexes
+sha'veh-kiriatha'im
+shall
+shammah
+share
+shaul
+shaved
+shaveh
+she
+she-asses
+she-goat
+she-goats
+sheaf
+shear
+sheaves
+sheba
+shechem
+shechem's
+shed
+sheds
+sheep
+sheepfolds
+sheepshearers
+shekel
+shekels
+shelah
+sheleph
+shelter
+shem
+sheme'ber
+sheol
+shepherd
+shepherding
+shepherds
+shepho
+shibah
+shield
+shillem
+shimron
+shinab
+shinar
+ships
+shobal
+shore
+short
+shortly
+shot
+should
+shoulder
+shoulders
+show
+showed
+shown
+shua
+shua's
+shuah
+shuni
+shur
+shut
+siddim
+side
+sidon
+sight
+sign
+signet
+signs
+silence
+silver
+simeon
+sin
+since
+sinew
+sinites
+sinned
+sinners
+sinning
+sister
+sister's
+sit
+sitnah
+sitting
+six
+sixteen
+sixth
+sixty
+sixty-five
+sixty-nine
+sixty-six
+sixty-two
+skilful
+skin
+skins
+slain
+slaughter
+slave
+slaves
+slay
+slays
+sleek
+sleep
+sleeves
+slept
+slew
+slope
+slowly
+small
+smell
+smelled
+smoke
+smoking
+smooth
+so
+sodom
+soil
+sojourn
+sojourned
+sojourner
+sojourners
+sojourning
+sojournings
+sold
+sole
+solemnly
+some
+son
+son's
+songs
+sons
+sons'
+sons-in-law
+soon
+sore
+sorely
+sorrow
+sorrowful
+sorry
+sort
+sorts
+sought
+soul
+sound
+south
+southward
+sow
+sowed
+space
+spare
+spared
+speak
+speaking
+speaks
+speckled
+speech
+spend
+spent
+spies
+spilled
+spirit
+splendor
+spoil
+spoke
+spoken
+spotted
+spread
+spring
+springing
+springs
+sprouted
+sprung
+staff
+stage
+stalk
+stand
+standing
+stands
+stars
+state
+states
+states:
+station
+statute
+statutes
+stay
+stayed
+stead
+steadfast
+steal
+steward
+still
+stole
+stolen
+stone
+stones
+stood
+stooped
+stop
+stopped
+store
+stored
+storehouses
+story
+stranger
+strangers
+straw
+streaks
+stream
+street
+strength
+stretched
+strife
+striking
+striped
+stripped
+striven
+strong
+stronger
+strongly
+struck
+struggled
+subdue
+subdued
+subject
+submit
+submitted
+subsided
+substance
+subtle
+success
+successful
+succoth
+such
+suck
+suckle
+suffer
+sufferable
+sufferance
+suitable
+summer
+summoned
+sun
+superior
+supplanted
+supplied
+support
+suppose
+supreme
+surely
+surety
+surrounded
+survivors
+suspended
+suspending
+sustained
+swallowed
+swarm
+swarming
+swarms
+swear
+sweat
+sword
+swords
+swore
+sworn
+system
+systems
+table
+tahash
+take
+taken
+taking
+talked
+talking
+tamar
+tamarisk
+tambourine
+tarried
+tarry
+tarshish
+taxes
+te'manites
+tebah
+teeth
+tell
+tells
+tema
+teman
+ten
+tender
+tenderly
+tent
+tenth
+tents
+tenure
+terah
+terms:
+territory
+terror
+tested
+than
+that
+the
+thee
+their
+theirs
+them
+themselves
+then
+thence
+there
+therefore
+therein
+these
+they
+thicket
+thigh
+thin
+thing
+things
+thinking
+third
+thirteen
+thirteenth
+thirty
+thirty-five
+thirty-four
+thirty-seven
+thirty-three
+thirty-two
+this
+thistles
+thorns
+thoroughly
+those
+thou
+thought
+thoughts
+thousand
+thousands
+thread
+three
+threshing
+throne
+through
+throughout
+throw
+thus
+thy
+tidal
+tidings
+ties
+tigris
+till
+tiller
+time
+times
+timna
+timnah
+tiras
+to
+today
+togar'mah
+together
+toil
+tola
+told
+tomb
+tonight
+too
+took
+top
+tops
+torch
+torn
+totally
+totaly
+touch
+touched
+touches
+toward
+tower
+towns
+trade
+traders
+train
+trained
+transgression
+transient
+transporting
+travailed
+treasure
+treat
+treated
+tree
+trees
+trembled
+trembling
+trial
+tribes
+tried
+troops
+trouble
+troubled
+troubles
+trough
+troughs
+truly
+truth
+truths
+tubal
+tubal-
+tubal-cain
+turn
+turned
+turns
+turtledove
+twelve
+twenty
+twenty-nine
+twenty-seven
+twenty-seventh
+twice
+twins
+two
+tyranny
+tyrant
+tyrants
+unacknowledged
+unalienable
+unawares
+uncircumcised
+uncomfortable
+uncovered
+under
+understand
+understood
+undistinguished
+unfit
+ungirded
+united
+unknown
+unleavened
+unless
+unmoved
+unstable
+until
+unusual
+unwarrantable
+unworth
+up
+upbraided
+upon
+uppermost
+upright
+ur
+urged
+us
+us:
+use
+using
+usurpations
+utterly
+uz
+uzal
+valley
+valuable
+vegetation
+veil
+vengeance
+vent
+vents
+verified
+very
+vesture
+villages
+vindication
+vine
+vineyard
+violence
+violently
+viper
+virgin
+vision
+visions
+visit
+visited
+voice
+void
+vow
+wages
+waging
+wagons
+wait
+waited
+walk
+walked
+walking
+wall
+wander
+wandered
+wanderer
+wandering
+wanting
+wantonness
+war
+warfare
+warned
+was
+wash
+washed
+washes
+watch
+water
+watered
+watering
+waters
+way
+wayside
+we
+weak
+weakness
+wealth
+wealthy
+weaned
+weapons
+wear
+wearied
+weary
+week
+weep
+weeping
+weighed
+weighing
+weight
+weights
+welfare
+well
+well's
+wells
+went
+wept
+were
+west
+westward
+what
+whatever
+whatsoever
+wheat
+whelp
+when
+whence
+whenever
+where
+whereby
+wherever
+whether
+which
+while
+white
+who
+whoever
+whole
+wholesome
+whom
+whomever
+whose
+why
+wicked
+wickedly
+wickedness
+widow
+widow's
+widowhood
+wife
+wife's
+wild
+wilderness
+will
+willing
+wilt
+wind
+window
+windows
+wine
+winged
+winter
+wise
+with
+withered
+withheld
+withhold
+within
+without
+witness
+wives
+wolf
+woman
+woman's
+womb
+wombs
+women
+wood
+word
+words
+wore
+work
+works
+world
+world:
+worse
+worship
+worshiped
+worth
+worthy
+would
+wounding
+wrapping
+wrath
+wrestled
+wrestlings
+wrong
+wrought
+year
+yearned
+years
+yes
+yet
+yield
+yielding
+yoke
+yonder
+you
+young
+younger
+youngest
+your
+yours
+yourself
+yourselves
+youth
+za'avan
+zaph'enath-pane'ah
+zeb'ulun
+zeboi'im
+zem'arites
+zepho
+zerah
+zib'eon
+zillah
+zilpah
+zimran
+ziph'ion
+zo'ar
+zohar
+zuzim
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..15f266a
--- /dev/null
+++ b/src/com/ibm/demo/DemoUtility.java
@@ -0,0 +1,136 @@
+/*
+ *******************************************************************************
+ * 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/04/26 18:40:15 $ 
+ * $Revision: 1.3 $
+ *
+ *****************************************************************************************
+ */
+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);
+    }
+    
+    /**
+     * Compares two locals. Return value is negative
+     * if they're different, and more positive the more
+     * fields that match.
+     */
+     
+    public static int compareLocales(Locale l1, Locale l2)
+    {
+        int result = -1;
+        
+        if (l1.getLanguage().equals(l2.getLanguage())) {
+            result += 1;
+            
+            if (l1.getCountry().equals(l2.getCountry())) {
+                result += 1;
+                
+                if (l1.getVariant().equals(l2.getVariant())) {
+                    result += 1;
+                }
+            }
+        }
+        
+        return result;
+    }
+    
+    /**
+     * 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..4ff0d48
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarApp.java
@@ -0,0 +1,45 @@
+/*
+ *******************************************************************************
+ * 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/05/12 23:21:23 $ 
+ * $Revision: 1.5 $
+ *
+ *****************************************************************************************
+ */
+
+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 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..b3ab269
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarCalc.java
@@ -0,0 +1,572 @@
+/*
+ *******************************************************************************
+ * 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/10/19 00:27:16 $ 
+ * $Revision: 1.8 $
+ *
+ *****************************************************************************************
+ */
+
+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 com.ibm.text.DateFormat;
+import java.text.ParsePosition;
+
+//import java.util.Calendar;
+import com.ibm.util.Calendar;
+//import java.util.GregorianCalendar;
+import com.ibm.util.GregorianCalendar;
+//import java.util.TimeZone;
+import com.ibm.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();
+            Locale defaultLocale = Locale.getDefault();
+            int bestMatch = -1, thisMatch = -1;
+            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());
+                }
+                
+                thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale);
+                
+                if (thisMatch >= bestMatch) {
+                    bestMatch = thisMatch;
+                    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, Calendar cal)
+        {
+            name = nameStr;
+            calendar = cal;
+            rollAdd = new Checkbox();
+
+            text = new JTextField("",FIELD_COLUMNS);
+            text.setFont(DemoUtility.editFont);
+
+            format = DateFormat.getDateInstance(cal, DateFormat.FULL,
+                                                Locale.getDefault());
+            //format.applyPattern(DEFAULT_FORMAT);
+        }
+
+        public void setLocale(Locale loc) {
+            String pattern = toPattern();
+
+            format = DateFormat.getDateInstance(calendar, DateFormat.FULL,
+                                                loc);
+            applyPattern(pattern);
+        }
+
+        public void applyPattern(String pattern) {
+            if (format instanceof SimpleDateFormat) {
+                ((SimpleDateFormat)format).applyPattern(pattern);
+//hey {al} - 
+//            } else if (format instanceof java.text.SimpleDateFormat) {
+//                ((java.text.SimpleDateFormat)format).applyPattern(pattern);
+            }
+        }
+        
+        private String toPattern() {
+            if (format instanceof SimpleDateFormat) {
+                return ((SimpleDateFormat)format).toPattern();
+//hey {al} - 
+//            } else if (format instanceof java.text.SimpleDateFormat) {
+//                return ((java.text.SimpleDateFormat)format).toPattern();
+            } else {
+                return "";
+            }
+        }
+
+        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;
+};
diff --git a/src/com/ibm/demo/calendar/CalendarFrame.java b/src/com/ibm/demo/calendar/CalendarFrame.java
new file mode 100755
index 0000000..9b400fe
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarFrame.java
@@ -0,0 +1,435 @@
+/*
+ *******************************************************************************
+ * 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/10/19 00:27:16 $ 
+ * $Revision: 1.8 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.demo.calendar;
+
+import com.ibm.demo.*;
+import com.ibm.util.Calendar;
+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 com.ibm.util.SimpleTimeZone;
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.io.*;
+//import java.text.DateFormat;
+import com.ibm.text.DateFormat;
+import java.text.MessageFormat;
+//import java.util.Calendar;
+import com.ibm.util.Calendar;
+import java.util.Date;
+//import java.util.GregorianCalendar;
+import com.ibm.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+//import java.util.TimeZone;
+import com.ibm.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();
+            Locale defaultLocale = Locale.getDefault();
+            int bestMatch = -1, thisMatch = -1;
+            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()))
+                {
+                    displayMenu.addItem( locales[i].getDisplayName() );
+                } else {
+                    displayMenu.addItem( locales[i].getDisplayLanguage());
+                }
+
+                thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale);
+                
+                if (thisMatch >= bestMatch) {
+                    bestMatch = thisMatch;
+                    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 = DateFormat.getDateTimeInstance(
+                                                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"));
+//hey {al} -
+//                        } 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, Calendar cal)
+        {
+            name = nameStr;
+            calendar = cal;
+        }
+
+        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..1b97dcb
--- /dev/null
+++ b/src/com/ibm/demo/calendar/CalendarPanel.java
@@ -0,0 +1,368 @@
+/*
+ *******************************************************************************
+ * 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/10/19 00:27:16 $ 
+ * $Revision: 1.6 $
+ *
+ *****************************************************************************************
+ */
+
+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 com.ibm.text.DateFormat;
+//import java.util.SimpleTimeZone;
+import com.ibm.util.SimpleTimeZone;
+//import java.util.*;
+import java.util.Date;
+import java.util.Locale;
+
+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] = new DateFormatSymbols(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] = new DateFormatSymbols(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/calendar/package.html b/src/com/ibm/demo/calendar/package.html
new file mode 100755
index 0000000..5275b89
--- /dev/null
+++ b/src/com/ibm/demo/calendar/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/calendar/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 06:28:48 $
+-->
+</head>
+<body bgcolor="white">
+Calendar demo applications including date/time arithmetic.
+</body>
+</html>
\ No newline at end of file
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..b21aacc
--- /dev/null
+++ b/src/com/ibm/demo/holiday/HolidayCalendarDemo.java
@@ -0,0 +1,720 @@
+/*
+ *******************************************************************************
+ * 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/05/12 23:21:32 $ 
+ * $Revision: 1.5 $
+ *
+ *****************************************************************************************
+ */
+
+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 com.ibm.text.SimpleDateFormat;
+import java.text.DateFormatSymbols;
+//import java.util.SimpleTimeZone;
+import com.ibm.util.SimpleTimeZone;
+import java.util.Locale;
+import java.util.Vector;
+import java.util.Date;
+
+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();
+        	
+        	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()))
+            	{
+                	displayMenu.addItem( locales[i].getDisplayName() );
+            	} else {
+                	displayMenu.addItem( locales[i].getDisplayLanguage());
+            	}
+            	
+            	if (locales[i].equals(kFirstLocale)) {
+            	    selectMe = i;
+            	}
+        	}
+        	
+        	displayMenu.setBackground(DemoUtility.choiceColor);
+        	displayMenu.select(selectMe);
+
+        	// 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 com.ibm.util.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/holiday/package.html b/src/com/ibm/demo/holiday/package.html
new file mode 100755
index 0000000..82ca145
--- /dev/null
+++ b/src/com/ibm/demo/holiday/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/holiday/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 06:54:50 $
+-->
+</head>
+<body bgcolor="white">
+Holiday demo application.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/ibm/demo/package.html b/src/com/ibm/demo/package.html
new file mode 100755
index 0000000..045c6c3
--- /dev/null
+++ b/src/com/ibm/demo/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 17:43:40 $
+-->
+</head>
+<body bgcolor="white">
+Shared utilities for demo applications and Applets.
+</body>
+</html>
\ No newline at end of file
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..8099ce2
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/BreakIteratorRules_en_US_DEMO.java
@@ -0,0 +1,223 @@
+/*
+ *******************************************************************************
+ * 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: 2001/02/20 22:50:12 $ 
+ * $Revision: 1.6 $
+ *
+ *****************************************************************************************
+ */
+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_DEMO extends ListResourceBundle {
+    
+    private static final URL url =
+        BreakIteratorRules_en_US_DEMO.class.getResource("/com/ibm/data/misc/english.dict");
+    
+    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 non-spacing marks, enclosing marks, and format characters,
+            // all of which should not influence the algorithm
+            "$_ignore_=[[:Mn:][:Me:][:Cf:]];"
+
+            // lower and upper case Roman letters, apostrophy and dash are
+            // in the English dictionary
+            + "$_dictionary_=[a-zA-Z\\'\\-];"
+
+            // Hindi phrase separator, kanji, katakana, hiragana, CJK diacriticals,
+            // other letters, and digits
+            + "$danda=[\u0964\u0965];"
+            + "$kanji=[\u3005\u4e00-\u9fa5\uf900-\ufa2d];"
+            + "$kata=[\u3099-\u309c\u30a1-\u30fe];"
+            + "$hira=[\u3041-\u309e\u30fc];"
+            + "$let=[[[:L:][:Mc:]]-[$kanji$kata$hira]];"
+            + "$dgt=[:N:];"
+
+            // punctuation that can occur in the middle of a word: currently
+            // dashes, apostrophes, and quotation marks
+            + "$mid_word=[[:Pd:]\u00ad\u2027\\\"\\\'];"
+
+            // punctuation that can occur in the middle of a number: currently
+            // apostrophes, qoutation marks, periods, commas, and the Arabic
+            // decimal point
+            + "$mid_num=[\\\"\\\'\\,\u066b\\.];"
+
+            // punctuation that can occur at the beginning of a number: currently
+            // the period, the number sign, and all currency symbols except the cents sign
+            + "$pre_num=[[[:Sc:]-[\u00a2]]\\#\\.];"
+
+            // punctuation that can occur at the end of a number: currently
+            // the percent, per-thousand, per-ten-thousand, and Arabic percent
+            // signs, the cents sign, and the ampersand
+            + "$post_num=[\\%\\&\u00a2\u066a\u2030\u2031];"
+
+            // line separators: currently LF, FF, PS, and LS
+            + "$ls=[\n\u000c\u2028\u2029];"
+
+            // whitespace: all space separators and the tab character
+            + "$ws=[[:Zs:]\t];"
+
+            // a word is a sequence of letters that may contain internal
+            // punctuation, as long as it begins and ends with a letter and
+            // never contains two punctuation marks in a row
+            + "$word=($let+($mid_word$let+)*$danda?);"
+
+            // a number is a sequence of digits that may contain internal
+            // punctuation, as long as it begins and ends with a digit and
+            // never contains two punctuation marks in a row.
+            + "$number=($dgt+($mid_num$dgt+)*);"
+
+            // break after every character, with the following exceptions
+            // (this will cause punctuation marks that aren't considered
+            // part of words or numbers to be treated as words unto themselves)
+            + ".;"
+
+            // keep together any sequence of contiguous words and numbers
+            // (including just one of either), plus an optional trailing
+            // number-suffix character
+            + "$word?($number$word)*($number$post_num?)?;"
+
+            // keep together and sequence of contiguous words and numbers
+            // that starts with a number-prefix character and a number,
+            // and may end with a number-suffix character
+            + "$pre_num($number$word)*($number$post_num?)?;"
+
+            // keep together runs of whitespace (optionally with a single trailing
+            // line separator or CRLF sequence)
+            + "$ws*\r?$ls?;"
+
+            // keep together runs of Katakana
+            + "$kata*;"
+
+            // keep together runs of Hiragana
+            + "$hira*;"
+
+            // keep together runs of Kanji
+            + "$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 non-spacing marks, enclosing marks, and format characters
+            "$_ignore_=[[:Mn:][:Me:][:Cf:]];"
+
+            // lower and upper case Roman letters, apostrophy and dash
+            // are in the English dictionary
+            + "$_dictionary_=[a-zA-Z\\'\\-];"
+
+            // Hindi phrase separators
+            + "$danda=[\u0964\u0965];"
+
+            // characters that always cause a break: ETX, tab, LF, FF, LS, and PS
+            + "$break=[\u0003\t\n\f\u2028\u2029];"
+
+            // characters that always prevent a break: the non-breaking space
+            // and similar characters
+            + "$nbsp=[\u00a0\u2007\u2011\ufeff];"
+
+            // whitespace: space separators and control characters, except for
+            // CR and the other characters mentioned above
+            + "$space=[[[:Zs:][:Cc:]]-[$nbsp$break\r]];"
+
+            // dashes: dash punctuation and the discretionary hyphen, except for
+            // non-breaking hyphens
+            + "$dash=[[[:Pd:]\u00ad]-[$nbsp]];"
+
+            // characters that stick to a word if they precede it: currency symbols
+            // (except the cents sign) and starting punctuation
+            + "$pre_word=[[[:Sc:]-[\u00a2]][:Ps:]\\\"\\\'];"
+
+            // characters that stick to a word if they follow it: ending punctuation,
+            // other punctuation that usually occurs at the end of a sentence,
+            // small Kana characters, some CJK diacritics, etc.
+            + "$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\uff0c"
+                    + "\uff0e\uff1f];"
+
+            // Kanji: actually includes both Kanji and Kana, except for small Kana and
+            // CJK diacritics
+            + "$kanji=[[\u4e00-\u9fa5\uf900-\ufa2d\u3041-\u3094\u30a1-\u30fa]-[$post_word$_ignore_]];"
+
+            // digits
+            + "$digit=[[:Nd:][:No:]];"
+
+            // punctuation that can occur in the middle of a number: periods and commas
+            + "$mid_num=[\\.\\,];"
+
+            // everything not mentioned above, plus the quote marks (which are both
+            // <pre-word>, <post-word>, and <char>)
+            + "$char=[^$break$space$dash$kanji$nbsp$_ignore_$pre_word$post_word$mid_num$danda\r\\\"\\\'];"
+
+            // a "number" is a run of prefix characters and dashes, followed by one or
+            // more digits with isolated number-punctuation characters interspersed
+            + "$number=([$pre_word$dash]*$digit+($mid_num$digit+)*);"
+
+            // the basic core of a word can be either a "number" as defined above, a single
+            // "Kanji" character, or a run of any number of not-explicitly-mentioned
+            // characters (this includes Latin letters)
+            + "$word_core=([$pre_word$char]*|$kanji|$number);"
+
+            // a word may end with an optional suffix that be either a run of one or
+            // more dashes or a run of word-suffix characters, followed by an optional
+            // run of whitespace
+            + "$word_suffix=(($dash+|$post_word*)$space*);"
+
+            // a word, thus, is an optional run of word-prefix characters, followed by
+            // a word core and a word suffix (the syntax of <word-core> and <word-suffix>
+            // actually allows either of them to match the empty string, putting a break
+            // between things like ")(" or "aaa(aaa"
+            + "$word=($pre_word*$word_core$word_suffix);"
+
+            // finally, the rule that does the work: Keep together any run of words that
+            // are joined by runs of one of more non-spacing mark.  Also keep a trailing
+            // line-break character or CRLF combination with the word.  (line separators
+            // "win" over nbsp's)
+            + "$word($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/demo/rbbi/DBBIDemo.java b/src/com/ibm/demo/rbbi/DBBIDemo.java
new file mode 100755
index 0000000..90bdb1e
--- /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: 2001/02/20 22:48:19 $ 
+ * $Revision: 1.6 $
+ *
+ *****************************************************************************************
+ */
+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,andtotallyunworthy"
++ "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("getWordInstance");
+            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/RBBIDemo.java b/src/com/ibm/demo/rbbi/RBBIDemo.java
new file mode 100755
index 0000000..83ab45b
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/RBBIDemo.java
@@ -0,0 +1,397 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2000, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbbi/Attic/RBBIDemo.java,v $ 
+ * $Date: 2000/09/26 21:46:54 $ 
+ * $Revision: 1.1 $
+ *
+ *****************************************************************************************
+ */
+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 RBBIDemo extends DemoApplet
+{
+    public static void main(String argv[]) {
+        Locale.setDefault(new Locale("en", "US"));
+		new RBBIDemo().showDemo();
+    }
+
+    public Frame createDemoFrame(DemoApplet applet) {
+        return new RBBIFrame(applet);
+    }
+}
+
+
+
+class RBBIFrame 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 RBBIFrame(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("Deva 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(new Font("Devanagari MT for IBM", Font.PLAIN, 48));
+            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
+            "\u0936\u094d\u0930\u0940\u092e\u0926\u094d " +
+            "\u092d\u0917\u0935\u0926\u094d\u0917\u0940\u0924\u093e " +
+            "\u0905\u0927\u094d\u092f\u093e\u092f " +
+            "\u0905\u0930\u094d\u091c\u0941\u0928 " +
+            "\u0935\u093f\u0937\u093e\u0926 " +
+            "\u092f\u094b\u0917 " +
+            "\u0927\u0943\u0924\u0930\u093e\u0937\u094d\u091f\u094d\u0930 " +
+            "\u0909\u0935\u093E\u091A\u0943 " +
+            "\u0927\u0930\u094d\u092e\u0915\u094d\u0937\u0947\u0924\u094d\u0930\u0947 " +
+            "\u0915\u0941\u0930\u0941\u0915\u094d\u0937\u0947\u0924\u094d\u0930\u0947 " +
+            "\u0938\u092e\u0935\u0947\u0924\u093e " +
+            "\u092f\u0941\u092f\u0941\u0924\u094d\u0938\u0935\u0903 " +
+            "\u092e\u093e\u092e\u0915\u093e\u0903 " +
+            "\u092a\u093e\u0923\u094d\u0921\u0935\u093e\u0936\u094d\u091a\u0948\u0935 " +
+            "\u0915\u093f\u092e\u0915\u0941\u0930\u094d\u0935\u0924 " +
+            "\u0938\u0902\u091c\u0935";
+    }
+
+
+    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/package.html b/src/com/ibm/demo/rbbi/package.html
new file mode 100755
index 0000000..d5eb46a
--- /dev/null
+++ b/src/com/ibm/demo/rbbi/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbbi/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 17:43:53 $
+-->
+</head>
+<body bgcolor="white">
+RuleBasedBreakIterator and DictionaryBasedBreakIterator demo applications.
+</body>
+</html>
\ No newline at end of file
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/rbnf/package.html b/src/com/ibm/demo/rbnf/package.html
new file mode 100755
index 0000000..48917ae
--- /dev/null
+++ b/src/com/ibm/demo/rbnf/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/rbnf/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 17:44:02 $
+-->
+</head>
+<body bgcolor="white">
+RuleBasedNumberFormat demo appliation.
+</body>
+</html>
\ No newline at end of file
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..c655dc9
--- /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>icu4j/src</code>, and make sure the Java files in the directories
+under <code>icu4j/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/demo/translit/package.html b/src/com/ibm/demo/translit/package.html
new file mode 100755
index 0000000..3f4e5b9
--- /dev/null
+++ b/src/com/ibm/demo/translit/package.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--  Copyright (C) 2000, International Business Machines Corporation and
+  others. All Rights Reserved.
+
+  $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/demo/translit/Attic/package.html,v $
+  $Revision: 1.1 $
+  $Date: 2000/03/15 17:44:12 $
+-->
+</head>
+<body bgcolor="white">
+Transliterator demo appliation.
+</body>
+</html>
\ No newline at end of file
diff --git a/src/com/ibm/icu/internal/UInfo.java b/src/com/ibm/icu/internal/UInfo.java
new file mode 100755
index 0000000..098dca9
--- /dev/null
+++ b/src/com/ibm/icu/internal/UInfo.java
@@ -0,0 +1,608 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 1996-2001, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ *
+ * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/internal/Attic/UInfo.java,v $ 
+ * $Date: 2001/03/15 23:31:12 $ 
+ * $Revision: 1.1 $
+ *
+ *****************************************************************************************
+ */
+
+package com.ibm.icu.internal;
+
+import java.io.*;
+import java.util.*;
+import com.ibm.util.Utility;
+
+public final class UInfo {
+    static final boolean DEBUG = false;
+    static final int UINFO_VERSION = 5;
+
+    // these values are aligned with the java.lang.Character constants
+
+    public static final byte
+    UNASSIGNED      = 0,
+    UPPERCASE_LETTER    = 1,
+    LOWERCASE_LETTER    = 2,
+    TITLECASE_LETTER    = 3,
+    MODIFIER_LETTER     = 4,
+    OTHER_LETTER        = 5,
+    NON_SPACING_MARK    = 6,
+    ENCLOSING_MARK      = 7,
+    COMBINING_SPACING_MARK  = 8,
+    DECIMAL_DIGIT_NUMBER    = 9,
+    LETTER_NUMBER       = 10,
+    OTHER_NUMBER        = 11,
+    SPACE_SEPARATOR     = 12,
+    LINE_SEPARATOR      = 13,
+    PARAGRAPH_SEPARATOR = 14,
+    CONTROL         = 15,
+    FORMAT          = 16,
+    PRIVATE_USE     = 18,
+    SURROGATE       = 19,
+    DASH_PUNCTUATION    = 20,
+    START_PUNCTUATION   = 21,
+    END_PUNCTUATION     = 22,
+    CONNECTOR_PUNCTUATION   = 23,
+    OTHER_PUNCTUATION   = 24,
+    MATH_SYMBOL     = 25,
+    CURRENCY_SYMBOL     = 26,
+    MODIFIER_SYMBOL     = 27,
+    OTHER_SYMBOL        = 28;
+
+    public String getName(char ch) {return getInfo(ch).name;}
+    public String getDecomposition(char ch) {return getInfo(ch).decomposition;}
+    public String getName10(char ch) {return getInfo(ch).name10;}
+    public String getComment(char ch) {return getInfo(ch).comment;}
+
+    public float getNumeric(char ch) {return getInfo(ch).numeric;}
+
+    public short getCanonicalClass(char ch) {return getInfo(ch).canonical;}
+    public short getDecimal(char ch) {return getInfo(ch).decimal;}
+    public short getDigit(char ch) {return getInfo(ch).digit;}
+
+    public char getUppercase(char ch) {return getInfo(ch).uppercase;}
+    public char getLowercase(char ch) {return getInfo(ch).lowercase;}
+    public char getTitlecase(char ch) {return getInfo(ch).titlecase;}
+
+    public byte getCategory(char ch) {return getInfo(ch).category;}
+    public byte getBidiClass(char ch) {return getInfo(ch).bidi;}
+    public boolean getMirrored(char ch) {return getInfo(ch).mirrored;}
+
+    public boolean isDisparaged(char ch) { return getDecomposition(ch).length() == 4; }
+
+    public boolean isLetter(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<UPPERCASE_LETTER)
+          | (1<<LOWERCASE_LETTER)
+          | (1<<TITLECASE_LETTER)
+          | (1<<MODIFIER_LETTER)
+          | (1<<MODIFIER_LETTER))));
+    }
+
+    public boolean isMark(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<NON_SPACING_MARK)
+          | (1<<ENCLOSING_MARK)
+          | (1<<COMBINING_SPACING_MARK))));
+    }
+
+    public boolean isNumber(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<DECIMAL_DIGIT_NUMBER)
+          | (1<<LETTER_NUMBER)
+          | (1<<OTHER_NUMBER))));
+    }
+
+    public boolean isSeparator(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<SPACE_SEPARATOR)
+          | (1<<LINE_SEPARATOR)
+          | (1<<PARAGRAPH_SEPARATOR))));
+    }
+
+    public boolean isFormat(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<CONTROL)
+          | (1<<FORMAT))));
+    }
+
+    public boolean isPunctuation(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<DASH_PUNCTUATION)
+          | (1<<START_PUNCTUATION)
+          | (1<<END_PUNCTUATION)
+          | (1<<CONNECTOR_PUNCTUATION)
+          | (1<<START_PUNCTUATION)
+          | (1<<END_PUNCTUATION)
+          | (1<<OTHER_PUNCTUATION))));
+    }
+
+    public boolean isSymbol(char ch) {
+        return (0 != ((1<<getCategory(ch)) &
+          ((1<<MATH_SYMBOL)
+          | (1<<CURRENCY_SYMBOL)
+          | (1<<MODIFIER_SYMBOL)
+          | (1<<OTHER_SYMBOL))));
+    }
+
+    //
+    // Characters excluded from composition.  This is read from the Unicode
+    // file CompositionExclusions.txt.
+    //
+    String composeExclude = "";
+
+    /**
+     * Is this character excluded from the composition algorithm by virtue
+     * of being listed in the composition exclusion table in Tech Report #15?
+     */
+    public boolean isExcludedComposition(char ch) {
+        return isDisparaged(ch)
+            || composeExclude.indexOf(ch) >= 0
+            || getCanonicalClass(getDecompositionChars(ch).charAt(0)) != 0;
+    }
+
+
+
+    public String getName(String s) {
+        return getName(s,true);
+    }
+
+    public String getName(String s, boolean shortVersion) {
+        StringBuffer temp = new StringBuffer();
+        for (int i = 0; i < s.length(); ++i) {
+            if (i != 0) temp.append(", ");
+            temp.append(getName(s.charAt(i), shortVersion));
+        }
+        return temp.toString();
+    }
+
+    public String getName(char ch, boolean shortVersion) {
+        String result = getName(ch);
+        if (!shortVersion) return result;
+        result = replace(result,"LETTER ","");
+        result = replace(result,"CHARACTER ","");
+        result = replace(result,"SIGN ","");
+        result = replace(result,"CAPITAL ","UC ");
+        if (getCategory(ch) == LOWERCASE_LETTER)
+          result = replace(result,"SMALL ","LC ");
+        result = replace(result,"COMBINING ","-");
+        result = replace(result,"WITH ","");
+        result = replace(result,"AND ","");
+        result = replace(result,"VARIA","GRAVE");
+        result = replace(result,"OXIA","ACUTE");
+        result = replace(result,"VRACHY","BREVE");
+        result = replace(result,"VERTICAL LINE ABOVE","TONOS");
+        result = replace(result,"PSILI","SMOOTH");
+        result = replace(result,"DASIA","ROUGH");
+        result = replace(result,"COMMA ABOVE","SMOOTH");
+        result = replace(result,"REVERSED COMMA ABOVE","ROUGH");
+        result = replace(result,"YPOGEGRAMMENI","IOTA-SUB");
+        result = replace(result,"PROSGEGRAMMENI","IOTA-AD");
+        result = replace(result,"DIALYTIKA","DIAERESIS");
+        result = replace(result,"PERISPOMENI","CIRCUMFLEX");
+        result = replace(result,"VOICED SOUND MARK","VOICED SIGN");
+        result = replace(result,"PROLONGED SOUND MARK","VOICED SIGN");
+        result = replace(result,"KATAKANA-HIRAGANA","KANA");
+        result = replace(result,"COMPATIBILITY IDEOGRAPH-","");
+        result = replace(result,"CHOSEONG","INITIAL");
+        result = replace(result,"JUNGSEONG","MEDIAL");
+        result = replace(result,"JONGSEONG","FINAL");
+
+        return result.substring(0,1)
+          + result.substring(1,result.length()).toLowerCase();
+    }
+
+    public String replace(String source,
+      String replacee, String replacer) {
+        int p = source.indexOf(replacee);
+        if (p == -1) return source;
+        return source.substring(0,p)
+          + replacer
+          + source.substring(p+replacee.length(),source.length());
+    }
+
+    public boolean isCCS(String s) {
+        if (s.length() < 2) return false;
+        if (isMark(s.charAt(0))) return false;
+        for (int i = 1; i < s.length(); ++i) {
+            if (!isMark(s.charAt(i))) return false;
+        }
+        return true;
+    }
+
+    // combining base sequence := <cat_zero>+ <cat_pos>*
+    public boolean isCBS(String s) {
+        if (s.length() == 0) return false;
+        if (getCanonicalClass(s.charAt(0)) != 0) return false;
+        boolean gotGreater = false;
+        for (int i = 1; i < s.length(); ++i) {
+            if (getCanonicalClass(s.charAt(i)) == 0) {
+                if (gotGreater) return false;
+            } else {
+                gotGreater = true;
+            }
+        }
+        return true;
+    }
+
+    public boolean hasCanonicalDecomposition(char ch) {
+        String decomp = getDecomposition(ch);
+        return (decomp.length() != 0 && decomp.indexOf('<') == -1);
+    }
+
+    public boolean hasCompatibilityDecomposition(char ch) {
+        String decomp = getDecomposition(ch);
+        return (decomp.length() != 0 && decomp.indexOf('<') != -1);
+    }
+
+    public boolean isEquivalent(
+      String a, String b, boolean canonical) {
+        return getFullDecomposition(a, canonical).equals(
+          getFullDecomposition(b, canonical));
+    }
+
+    // use very dumb algorithm. Don't need lower order one.
+
+    public String getFullDecomposition(
+      String s, boolean canonical) {
+        StringBuffer output = new StringBuffer();
+        for (int i = 0; i < s.length(); ++i) {
+            getFullDecomp2(s.charAt(i),canonical,output);
+        }
+        return fixCanonical(output).toString();
+    }
+
+    public StringBuffer getFullDecomposition(
+      char ch, boolean canonical, StringBuffer output) {
+
+        StringBuffer result = getFullDecomp2(ch,canonical,output);
+        return fixCanonical(result);
+    }
+
+    public String getFullDecomposition(
+      char ch, boolean canonical) {
+        return getFullDecomposition(ch, canonical, new StringBuffer()).toString();
+    }
+
+    /**
+     * Given a decomposed string of characters, put it in canonical
+     * order by finding and processing all exchangeable pairs.
+     */
+    public StringBuffer fixCanonical(StringBuffer target) {
+        for (int i = 1; i < target.length(); ++i) {
+            char ch = target.charAt(i);
+            short canClass = getCanonicalClass(ch);
+            char chPrev = target.charAt(i-1);
+            short canClassPrev = getCanonicalClass(chPrev);
+            if (canClass != 0 && canClass < canClassPrev) {
+                target.setCharAt(i-1, ch);
+                target.setCharAt(i, chPrev);
+                if (i > 1) i -= 2; // backup (-1 to compensate for loop)
+            }
+        }
+        return target;
+    }
+
+    public String fixCanonical(String source) {
+        return fixCanonical(new StringBuffer(source)).toString();
+    }
+
+
+    // ============================================
+    //                  PRIVATES
+    // ============================================
+
+    static class CharData {
+        public CharData() {
+        };
+
+        String name = "";
+        String decomposition = "";
+        String name10 = "";
+        String comment = "";
+
+        float numeric = Float.MIN_VALUE;
+
+        short canonical = 0;
+        short decimal = Short.MIN_VALUE;
+        short digit = Short.MIN_VALUE;
+
+        char uppercase;
+        char lowercase;
+        char titlecase;
+
+        byte category;
+        byte bidi = 0;
+
+        boolean mirrored;
+    };
+
+    private static final CharData UNASSIGNED_INFO = new CharData();
+    private static char cachedChar = 0xFFFF;
+
+    private CharData getInfo(char ch) {
+        if (ch == cachedChar) return UNASSIGNED_INFO;
+        // remap special ranges
+        if (ch >= 0x4E00 && ch < 0xF900) {
+            if (ch <= 0x9FA5) ch = 0x4E00;
+            else if (ch >= 0xAC00 && ch <= 0xD7A3) ch = 0xAC00;
+            else if (ch >= 0xD800 && ch <= 0xDFFF) ch = 0xD800;
+            else if (ch >= 0xE000) ch = 0xE000;
+        }
+        Object value = cache[ch];
+        CharData result;
+        if (value == null) {
+            result = UNASSIGNED_INFO;
+        } else if (value instanceof String) {
+            result = updateCache((String)value);
+        } else {
+            result = (CharData)value;
+        }
+        return result;
+    }
+
+    private StringBuffer getFullDecomp2(
+      char ch, boolean canonical, StringBuffer output) {
+
+        String decomp = getDecomposition(ch);
+        if (decomp.length() == 0
+          || (canonical && decomp.indexOf('<') != -1)) {
+            output.append(ch);
+            return output;
+        }
+        boolean inBrackets = false;
+        for (int i = 0; i < decomp.length(); ++i) {
+            char c = decomp.charAt(i);
+            if (c == '<') inBrackets = true;
+            else if (c == '>') inBrackets = false;
+            else if (inBrackets) ; // skip
+            else if (c == ' ') ; // skip
+            else {
+                String tempString = decomp.substring(i,i+4);
+                char temp = (char)Integer.parseInt(tempString,16);
+                getFullDecomposition(temp,canonical,output);
+                i+= 3;
+            }
+        }
+        return output;
+    }
+
+    public String getDecompositionChars(char ch) {
+        StringBuffer output = new StringBuffer();
+        String decomp = getDecomposition(ch);
+        if (decomp.length() == 0) {
+            output.append(ch);
+            return output.toString();
+        }
+        boolean inBrackets = false;
+        for (int i = 0; i < decomp.length(); ++i) {
+            char c = decomp.charAt(i);
+            if (c == '<') inBrackets = true;
+            else if (c == '>') inBrackets = false;
+            else if (inBrackets) ; // skip
+            else if (c == ' ') ; // skip
+            else {
+                String tempString = decomp.substring(i,i+4);
+                char temp = (char)Integer.parseInt(tempString,16);
+                output.append(temp);
+                i+= 3;
+            }
+        }
+        return output.toString();
+    }
+
+    public UInfo(String fileName, String composeExcludeFileName) {
+        long startTime,endTime;
+
+        BufferedReader input = null;
+        String line = null;
+        try {
+            input = new BufferedReader(new FileReader(fileName),64*1024);
+            for (int count = 0;;++count) {
+                line = input.readLine();
+                if (line == null) break;
+                if (line.length() == 0) continue;
+                char ch = charFrom(line.substring(0,4));
+                if (DEBUG) if ((count % 100) == 0)
+                    System.out.println("[" + count + "," + Utility.hex(ch) + ']');
+                cache[ch] = line;
+            }
+
+            // Read composition exlusions
+            input = new BufferedReader(new FileReader(composeExcludeFileName),64*1024);
+            StringBuffer ce = new StringBuffer();
+            for (;;) {
+                line = input.readLine();
+                if (line == null) break;
+                if (line.length() == 0 ||
+                    Character.digit(line.charAt(0), 16) < 0) continue;
+                ce.append(charFrom(line.substring(0,4)));
+            }
+            composeExclude = ce.toString();
+        } catch (Exception ex) {
+            try {
+                input.close();
+            } catch (Exception ex2) {}
+            ex.printStackTrace();
+            throw new IllegalArgumentException("Couldn't read file "
+              + ex.getClass().getName() + " " + ex.getMessage()
+              + " line = " + line
+              );
+        }
+    }
+
+    public UInfo() {
+        // FIX
+        // This is bad...this path must be correct relative to the
+        // user's current directory.  I have changed it so that it's
+        // relative to the root icu4j directory, so it works as long
+        // as code is run from that directory, e.g., "java -classpath
+        // classes...".  A better way to do this might be to get it
+        // from a system property that is defined on the command line,
+        // e.g., "java -Dicu4j=D:/icu4j..." - liu
+        this("src/data/unicode/UnicodeData.txt",
+             "src/data/unicode/CompositionExclusions.txt");
+    }
+
+    /*
+  0 Code value in 4-digit hexadecimal format.
+  1 Unicode 2.1 Character Name. These names match exactly the
+  2 General Category. This is a useful breakdown into various "character
+  3 Canonical Combining Classes. The classes used for the
+  4 Bidirectional Category. See the list below for an explanation of the
+  5 Character Decomposition. In the Unicode Standard, not all of
+  6 Decimal digit value. This is a numeric field. If the character
+  7 Digit value. This is a numeric field. If the character represents a
+  8 Numeric value. This is a numeric field. If the character has the
+  9 If the characters has been identified as a "mirrored" character in
+ 10 Unicode 1.0 Name. This is the old name as published in Unicode 1.0.
+ 11 10646 Comment field. This field is informative.
+ 12 Upper case equivalent mapping. If a character is part of an
+ 13 Lower case equivalent mapping. Similar to 12. This field is informative.
+ 14 Title case equivalent mapping. Similar to 12. This field is informative.
+    */
+
+    private CharData updateCache(String line) {
+        try {
+            String[] parts = new String[30];
+            Utility.split(line,';',parts);
+            CharData info = new CharData();
+            char ch = charFrom(parts[0]);
+            info.name = parts[1];
+            info.category = (byte)Utility.lookup(parts[2], CATEGORY_TABLE);
+            info.canonical = shortFrom(parts[3]);
+            info.bidi = (byte)Utility.lookup(parts[4], BIDI_TABLE);
+            info.decomposition = parts[5];
+            info.decimal = shortFrom(parts[6]);
+            info.digit = shortFrom(parts[7]);
+            info.numeric = floatFrom(parts[8]);
+            info.mirrored = charFrom(parts[9]) == 'Y';
+            info.name10 = parts[10];
+            info.comment = parts[11];
+            info.uppercase = charFrom(parts[12]);
+            if (info.uppercase == 0) info.uppercase = ch;
+            info.lowercase = charFrom(parts[13]);
+            if (info.lowercase == 0) info.lowercase = ch;
+            info.titlecase = charFrom(parts[14]);
+            if (info.titlecase == 0) info.titlecase = info.uppercase;
+            String trial = Utility.hex(ch) + ";" + info;
+            if (DEBUG) if (!trial.equals(line)) {
+                System.out.println("Difference between:");
+                System.out.println(line);
+                System.out.println(trial);
+            }
+            cache[ch] = info;
+            return info;
+        }
+        catch (NumberFormatException e) {
+            System.out.println("updateCache: error parsing '" + line + "'");
+            throw e;
+        }
+    }
+
+    private static CharData typeInfo = new CharData();
+
+    private boolean latin1(char c) {
+        return ((c >= 20 && c <= 0x7F) || c > 0xA0);
+    }
+
+    private static final String[] YN_TABLE = {"N", "Y"};
+
+    private static final String[] CATEGORY_TABLE = {
+        "Cn", // = Other, Not Assigned
+
+        "Lu", // = Letter, Uppercase
+        "Ll", // = Letter, Lowercase
+        "Lt", // = Letter, Titlecase
+        "Lm", // = Letter, Modifier
+        "Lo", // = Letter, Other
+
+        "Mn", // = Mark, Non-Spacing
+        "Me", // = Mark, Enclosing
+        "Mc", // = Mark, Spacing Combining
+
+        "Nd", // = Number, Decimal Digit
+        "Nl", // = Number, Letter
+        "No", // = Number, Other
+
+        "Zs", // = Separator, Space
+        "Zl", // = Separator, Line
+        "Zp", // = Separator, Paragraph
+
+        "Cc", // = Other, Control
+        "Cf", // = Other, Format
+        "",   // unused
+        "Co", // = Other, Private Use
+        "Cs", // = Other, Surrogate
+
+
+        "Pd", // = Punctuation, Dash
+        "Ps", // = Punctuation, Open
+        "Pe", // = Punctuation, Close
+        "Pc", // = Punctuation, Connector
+        "Po", // = Punctuation, Other
+
+        "Sm", // = Symbol, Math
+        "Sc", // = Symbol, Currency
+        "Sk", // = Symbol, Modifier
+        "So", // = Symbol, Other
+
+        "Pi", // = Punctuation, Initial quote (may behave like Ps or Pe depending on usage)
+        "Pf", // = Punctuation, Final quote (may behave like Ps or Pe dependingon usage)
+    };
+
+    private static String[] BIDI_TABLE = {
+        "L", // Left-Right; Most alphabetic, syllabic, and logographic characters (e.g., CJK ideographs)
+        "R", // Right-Left; Arabic, Hebrew, and punctuation specific to those scripts
+        "EN", //    European Number
+        "ES", //    European Number Separator
+        "ET", //    European Number Terminator
+        "AN", //    Arabic Number
+        "CS", //    Common Number Separator
+        "B", // Block Separator
+        "S", // Segment Separator
+        "WS", //    Whitespace
+        "ON" // Other Neutrals ; All other characters: punctuation, symbols
+    };
+
+    private static short shortFrom(String p) {
+        if (p.length() == 0) return Short.MIN_VALUE;
+        return Short.parseShort(p);
+    }
+
+    private static float floatFrom(String p) {
+        try {
+            if (p.length() == 0) return Float.MIN_VALUE;
+            int fract = p.indexOf('/');
+            if (fract == -1) return Float.valueOf(p).floatValue();
+            String q = p.substring(0,fract);
+            float num = 0;
+            if (q.length() != 0) num = Integer.parseInt(q);
+            p = p.substring(fract+1,p.length());
+            float den = 0;
+            if (p.length() != 0) den = Integer.parseInt(p);
+            return num/den;
+        }
+        catch (NumberFormatException e) {
+            System.out.println("floatFrom: error parsing '" + p + "'");
+            throw e;
+        }
+    }
+
+    private static char charFrom(String p) {
+        if (p.length() == 0) return '\u0000';
+        else if (p.length() == 1) return p.charAt(0);
+        int temp = Integer.parseInt(p, 16);
+        if (temp < 0 || temp > 0xFFFF)
+            throw new NumberFormatException(
+                "Hex char out of range: " + p);
+        return (char)temp;
+    }
+
+
+    private Object[] cache = new Object[65536];
+}
diff --git a/src/com/ibm/icu/test/text/ArabicShapingRegTest.java b/src/com/ibm/icu/test/text/ArabicShapingRegTest.java
new file mode 100755
index 0000000..d1eebbb
--- /dev/null
+++ b/src/com/ibm/icu/test/text/ArabicShapingRegTest.java
@@ -0,0 +1,415 @@
+/*
+*******************************************************************************
+*   Copyright (C) 2001, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*******************************************************************************
+*/
+
+package com.ibm.icu.test.text;
+
+import com.ibm.test.TestFmwk;
+import com.ibm.text.ArabicShaping;
+import com.ibm.text.ArabicShapingException;
+
+/**
+ * Regression test for Arabic shaping.
+ */
+public class ArabicShapingRegTest extends TestFmwk {
+
+    /* constants copied from ArabicShaping for convenience */
+
+    public static final int LENGTH_GROW_SHRINK = 0;
+    public static final int LENGTH_FIXED_SPACES_NEAR = 1;
+    public static final int LENGTH_FIXED_SPACES_AT_END = 2;
+    public static final int LENGTH_FIXED_SPACES_AT_BEGINNING = 3;
+
+    public static final int TEXT_DIRECTION_LOGICAL = 0;
+    public static final int TEXT_DIRECTION_VISUAL_LTR = 4;
+
+    public static final int LETTERS_NOOP = 0;
+    public static final int LETTERS_SHAPE = 8;
+    public static final int LETTERS_UNSHAPE = 0x10;
+
+    public static final int DIGITS_NOOP = 0;
+    public static final int DIGITS_EN2AN = 0x20;
+    public static final int DIGITS_AN2EN = 0x40;
+    public static final int DIGITS_EN2AN_INIT_LR = 0x60;
+    public static final int DIGITS_EN2AN_INIT_AL = 0x80;
+    private static final int DIGITS_RESERVED = 0xa0;
+
+    public static final int DIGIT_TYPE_AN = 0;
+    public static final int DIGIT_TYPE_AN_EXTENDED = 0x100;
+
+    public static class TestData {
+        public int type;
+        public String source;
+        public int flags;
+        public String result;
+        public int length;
+        public Class error;
+
+        public static final int STANDARD = 0;
+        public static final int PREFLIGHT = 1;
+        public static final int ERROR = 2;
+
+        public static TestData standard(String source, int flags, String result) {
+            return new TestData(STANDARD, source, flags, result, 0, null);
+        }
+        
+        public static TestData preflight(String source, int flags, int length) {
+            return new TestData(PREFLIGHT, source, flags, null, length, null);
+        }
+
+        public static TestData error(String source, int flags, Class error) {
+            return new TestData(ERROR, source, flags, null, 0, error);
+        }
+
+        private TestData(int type, String source, int flags, String result, int length, Class error) {
+            this.type = type;
+            this.source = source;
+            this.flags = flags;
+            this.result = result;
+            this.length = length;
+            this.error = error;
+        }
+
+        private static final String[] typenames = { "standard", "preflight", "error" };
+
+        public String toString() {
+            StringBuffer buf = new StringBuffer(super.toString());
+            buf.append("[\n");
+            buf.append(typenames[type]);
+            buf.append(",\n");
+            if (source == null) {
+                buf.append("null");
+            } else {
+                buf.append('"');
+                buf.append(escapedString(source));
+                buf.append('"');
+            }
+            buf.append(",\n");
+            buf.append(Integer.toHexString(flags));
+            buf.append(",\n");
+            if (result == null) {
+                buf.append("null");
+            } else {
+                buf.append('"');
+                buf.append(escapedString(result));
+                buf.append('"');
+            }
+            buf.append(",\n");
+            buf.append(length);
+            buf.append(",\n");
+            buf.append(error);
+            buf.append(']');
+            return buf.toString();
+        }
+    }
+
+    private static final String lamAlefSpecialVLTR =
+        "\u0020\u0646\u0622\u0644\u0627\u0020" +
+     	"\u0646\u0623\u064E\u0644\u0627\u0020" +
+     	"\u0646\u0627\u0670\u0644\u0627\u0020" +
+     	"\u0646\u0622\u0653\u0644\u0627\u0020" +
+     	"\u0646\u0625\u0655\u0644\u0627\u0020" +
+     	"\u0646\u0622\u0654\u0644\u0627\u0020" +
+     	"\uFEFC\u0639";
+
+    private static final String tashkeelSpecialVLTR = 
+        "\u064A\u0628\u0631\u0639\u0020" +
+        "\u064A\u0628\u0651\u0631\u064E\u0639\u0020" +
+        "\u064C\u064A\u0628\u0631\u064F\u0639\u0020" +
+        "\u0628\u0670\u0631\u0670\u0639\u0020" +
+        "\u0628\u0653\u0631\u0653\u0639\u0020" +
+        "\u0628\u0654\u0631\u0654\u0639\u0020" +
+        "\u0628\u0655\u0631\u0655\u0639\u0020";
+
+    private static final String logicalUnshape = 
+        "\u0020\u0020\u0020\uFE8D\uFEF5\u0020\uFEE5\u0020\uFE8D\uFEF7\u0020" +
+        "\uFED7\uFEFC\u0020\uFEE1\u0020\uFE8D\uFEDF\uFECC\uFEAE\uFE91\uFEF4" +
+        "\uFE94\u0020\uFE8D\uFEDF\uFEA4\uFEAE\uFE93\u0020\u0020\u0020\u0020";
+
+    private static final String numSource =
+        "\u0031" +  /* en:1 */
+        "\u0627" +  /* arabic:alef */
+        "\u0032" +  /* en:2 */
+        "\u06f3" +  /* an:3 */
+        "\u0061" +  /* latin:a */
+        "\u0034";   /* en:4 */
+
+    private static final TestData[] tests = {
+        /* lam alef special visual ltr */
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
+                          "\u0020\ufee5\u0020\ufef5\ufe8d\u0020" +
+                          "\ufee5\u0020\ufe76\ufef7\ufe8d\u0020" +
+                          "\ufee5\u0020\u0670\ufefb\ufe8d\u0020" +
+                          "\ufee5\u0020\u0653\ufef5\ufe8d\u0020" +
+                          "\ufee5\u0020\u0655\ufef9\ufe8d\u0020" +
+                          "\ufee5\u0020\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_END,
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb\u0020\u0020\u0020\u0020" +
+                          "\u0020\u0020"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_BEGINNING,
+                          "\u0020\u0020\u0020\u0020\u0020\u0020" +
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_GROW_SHRINK,
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        /* TASHKEEL unsupported
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE_EXCEPT_TASHKEEL | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
+                          "\u0020\ufee5\u0020\ufef5\ufe8d\u0020" +
+                          "\ufee5\u0020\ufe76\ufef7\ufe8d\u0020" +
+                          "\ufee5\u0020\u0670\ufefb\ufe8d\u0020" +
+                          "\ufee5\u0020\u0653\ufef5\ufe8d\u0020" +
+                          "\ufee5\u0020\u0655\ufef9\ufe8d\u0020" +
+                          "\ufee5\u0020\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE_EXCEPT_TASHKEEL | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_END,
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb\u0020\u0020\u0020\u0020" +
+                          "\u0020\u0020"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE_EXCEPT_TASHKEEL | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_AT_BEGINNING,
+                          "\u0020\u0020\u0020\u0020\u0020\u0020" +
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        TestData.standard(lamAlefSpecialVLTR,
+                          LETTERS_SHAPE_EXCEPT_TASHKEEL | TEXT_DIRECTION_VISUAL_LTR | LENGTH_GROW_SHRINK,
+                          "\u0020\ufee5\ufef5\ufe8d\u0020\ufee5" +
+                          "\ufe76\ufef7\ufe8d\u0020\ufee5\u0670" +
+                          "\ufefb\ufe8d\u0020\ufee5\u0653\ufef5" +
+                          "\ufe8d\u0020\ufee5\u0655\ufef9\ufe8d" +
+                          "\u0020\ufee5\u0654\ufef5\ufe8d\u0020" +
+                          "\ufefc\ufecb"),
+        */
+
+        /* tashkeel special visual ltr */
+        TestData.standard(tashkeelSpecialVLTR,
+                          LETTERS_SHAPE | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
+                          "\ufef2\ufe91\ufeae\ufecb\u0020" +
+                          "\ufef2\ufe91\ufe7c\ufeae\ufe77\ufecb\u0020" +
+                          "\ufe72\ufef2\ufe91\ufeae\ufe79\ufecb\u0020" +
+                          "\ufe8f\u0670\ufeae\u0670\ufecb\u0020" +
+                          "\ufe8f\u0653\ufeae\u0653\ufecb\u0020" +
+                          "\ufe8f\u0654\ufeae\u0654\ufecb\u0020" +
+                          "\ufe8f\u0655\ufeae\u0655\ufecb\u0020"),
+        /* TASHKEEL unsupported
+        TestData.standard(tashkeelSpecialVLTR,
+                          LETTERS_SHAPE_EXCEPT_TASHKEEL | TEXT_DIRECTION_VISUAL_LTR | LENGTH_FIXED_SPACES_NEAR,
+                          "\ufef2\ufe91\ufeae\ufecb\u0020" +
+                          "\ufef2\ufe91\ufe7c\ufeae\ufe76\ufecb\u0020" +
+                          "\ufe72\ufef2\ufe91\ufeae\ufe78\ufecb\u0020" +
+                          "\ufe8f\u0670\ufeae\u0670\ufecb\u0020" +
+                          "\ufe8f\u0653\ufeae\u0653\ufecb\u0020" +
+                          "\ufe8f\u0654\ufeae\u0654\ufecb\u0020" +
+                          "\ufe8f\u0655\ufeae\u0655\ufecb\u0020"),
+        */
+
+        /* logical unshape */
+        TestData.standard(logicalUnshape,
+                          LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_NEAR,
+                          "\u0020\u0020\u0020\u0627\u0644\u0622\u0646\u0020\u0627\u0644\u0623\u0642\u0644\u0627" +
+                          "\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020\u0627\u0644\u062d\u0631" +
+                          "\u0629\u0020\u0020\u0020\u0020"),
+        TestData.standard(logicalUnshape,
+                          LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_AT_END,
+                          "\u0020\u0020\u0020\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623\u0020\u0642" +
+                          "\u0644\u0627\u0020\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020\u0627" +
+                          "\u0644\u062d\u0631\u0629\u0020"),
+        TestData.standard(logicalUnshape,
+                          LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_FIXED_SPACES_AT_BEGINNING,
+                          "\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623\u0020\u0642\u0644\u0627\u0020" +
+                          "\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020\u0627\u0644\u062d\u0631" +
+                          "\u0629\u0020\u0020\u0020\u0020"),
+        TestData.standard(logicalUnshape,
+                          LETTERS_UNSHAPE | TEXT_DIRECTION_LOGICAL | LENGTH_GROW_SHRINK,
+                          "\u0020\u0020\u0020\u0627\u0644\u0622\u0020\u0646\u0020\u0627\u0644\u0623\u0020\u0642" +
+                          "\u0644\u0627\u0020\u0645\u0020\u0627\u0644\u0639\u0631\u0628\u064a\u0629\u0020\u0627" +
+                          "\u0644\u062d\u0631\u0629\u0020\u0020\u0020\u0020"),
+
+        /* numbers */
+        TestData.standard(numSource, 
+                          DIGITS_EN2AN | DIGIT_TYPE_AN, 
+                          "\u0661\u0627\u0662\u06f3\u0061\u0664"),
+        TestData.standard(numSource, 
+                          DIGITS_AN2EN | DIGIT_TYPE_AN_EXTENDED, 
+                          "\u0031\u0627\u0032\u0033\u0061\u0034"),
+        TestData.standard(numSource, 
+                          DIGITS_EN2AN_INIT_LR | DIGIT_TYPE_AN, 
+                          "\u0031\u0627\u0662\u06f3\u0061\u0034"),
+        TestData.standard(numSource, 
+                          DIGITS_EN2AN_INIT_AL | DIGIT_TYPE_AN_EXTENDED, 
+                          "\u06f1\u0627\u06f2\u06f3\u0061\u0034"),
+        TestData.standard(numSource, 
+                          DIGITS_EN2AN_INIT_LR | DIGIT_TYPE_AN | TEXT_DIRECTION_VISUAL_LTR,
+                          "\u0661\u0627\u0032\u06f3\u0061\u0034"),
+        TestData.standard(numSource, 
+                          DIGITS_EN2AN_INIT_AL | DIGIT_TYPE_AN_EXTENDED | TEXT_DIRECTION_VISUAL_LTR,
+                          "\u06f1\u0627\u0032\u06f3\u0061\u06f4"),
+
+        /* no-op */
+        TestData.standard(numSource, 
+                          0,
+                          numSource),
+
+        /* preflight */
+        TestData.preflight("\u0644\u0627",
+                           LETTERS_SHAPE | LENGTH_GROW_SHRINK,
+                           1),
+
+        TestData.preflight("\u0644\u0627\u0031",
+                           DIGITS_EN2AN | DIGIT_TYPE_AN_EXTENDED | LENGTH_GROW_SHRINK,
+                           3),
+
+        TestData.preflight("\u0644\u0644",
+                           LETTERS_SHAPE | LENGTH_GROW_SHRINK,
+                           2),
+
+        TestData.preflight("\ufef7",
+                           LETTERS_UNSHAPE | LENGTH_GROW_SHRINK,
+                           2),
+
+        /* bad data */
+        TestData.error("\u0020\ufef7\u0644\u0020",
+                       LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_NEAR,
+                       ArabicShapingException.class),
+
+        TestData.error("\u0020\ufef7",
+                       LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_AT_END,
+                       ArabicShapingException.class),
+
+        TestData.error("\ufef7\u0020",
+                       LETTERS_UNSHAPE | LENGTH_FIXED_SPACES_AT_BEGINNING,
+                       ArabicShapingException.class),
+
+        /* bad options */
+        TestData.error("\ufef7",
+                       0xffffffff,
+                       IllegalArgumentException.class),
+    };
+
+    public void testStandard() {
+        for (int i = 0; i < tests.length; ++i) {
+            TestData test = tests[i];
+
+            Exception ex = null;
+            String result = null;
+            ArabicShaping shaper = null;
+            try {
+                shaper = new ArabicShaping(test.flags);
+                result = shaper.shape(test.source);
+            }
+            catch (Exception e) {
+                ex = e;
+            }
+            
+            switch (test.type) {
+            case TestData.STANDARD:
+                if (!test.result.equals(result)) {
+                    reportTestFailure(i, test, shaper, result, ex);
+                }
+                break;
+                
+            case TestData.PREFLIGHT:
+                if (result == null || test.length != result.length()) {
+                    reportTestFailure(i, test, shaper, result, ex);
+                }
+                break;
+
+            case TestData.ERROR:
+                if (!test.error.isInstance(ex)) {
+                    reportTestFailure(i, test, shaper, result, ex);
+                }
+                break;
+            }
+        }                    
+    }
+
+    public void reportTestFailure(int index, TestData test, ArabicShaping shaper, String result, Exception error) {
+        StringBuffer buf = new StringBuffer();
+        buf.append("*** test failure ***\n");
+        buf.append("index: " + index + "\n");
+        buf.append("test: " + test + "\n");
+        buf.append("shaper: " + shaper + "\n");
+        buf.append("result: " + escapedString(result) + "\n");
+        buf.append("error: " + error + "\n");
+
+        if (result != null && test.result != null && !test.result.equals(result)) {
+            for (int i = 0; i < Math.max(test.result.length(), result.length()); ++i) {
+                String temp = Integer.toString(i);
+                if (temp.length() < 2) {
+                    temp = " ".concat(temp);
+                }
+                char trg = i < test.result.length() ? test.result.charAt(i) : '\uffff';
+                char res = i < result.length() ? result.charAt(i) : '\uffff';
+
+                buf.append("[" + temp + "] ");
+                buf.append(escapedString("" + trg) + " ");
+                buf.append(escapedString("" + res) + " ");
+                if (trg != res) {
+                    buf.append("***");
+                }
+                buf.append("\n");
+            }
+        }
+        err(buf.toString());
+    }
+
+    private static String escapedString(String str) {
+        StringBuffer buf = new StringBuffer(str.length() * 6);
+        for (int i = 0; i < str.length(); ++i) {
+            char ch = str.charAt(i);
+            buf.append("\\u");
+            if (ch < 0x1000) {
+                buf.append('0');
+            }
+            if (ch < 0x0100) {
+                buf.append('0');
+            }
+            if (ch < 0x0010) {
+                buf.append('0');
+            }
+            buf.append(Integer.toHexString(ch));
+        }
+        return buf.toString();
+    }
+
+    public static void main(String[] args) {
+        try {
+            new ArabicShapingRegTest().run(args);
+        }
+        catch (Exception e) {
+            System.out.println(e);
+        }
+    }
+}
+
diff --git a/src/com/ibm/icu/test/text/ArabicShapingTest.java b/src/com/ibm/icu/test/text/ArabicShapingTest.java
new file mode 100755
index 0000000..670f5c6
--- /dev/null
+++ b/src/com/ibm/icu/test/text/ArabicShapingTest.java
@@ -0,0 +1,288 @@
+/*
+*******************************************************************************
+*   Copyright (C) 2001, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*******************************************************************************
+*/
+
+package com.ibm.icu.test.text;
+
+import com.ibm.text.ArabicShaping;
+import com.ibm.text.ArabicShapingException;
+
+/**
+ * Interactive test for Arabic shaping.
+ * Invoke from a command line passing args and strings.  Use '-help' to see description of arguments.
+ */
+public class ArabicShapingTest {
+    private static final int COPY = 0;
+    private static final int INPLACE = 1;
+    private static final int STRING = 2;
+
+    public static final void main(String[] args) {
+        int testtype = COPY;
+        int options = 0;
+        int ss = 0;
+        int sl = -1;
+        int ds = 0;
+        int dl = -1;
+        String text = "$22.4 test 123 \ufef6\u0644\u0622 456 \u0664\u0665\u0666!";
+
+        for (int i = 0; i < args.length; ++i) {
+            String arg = args[i];
+            if (arg.charAt(0) == '-') {
+                String opt = arg.substring(1);
+                String val = opt;
+                int index = arg.indexOf(':');
+                if (index != -1) {
+                    opt = opt.substring(0, Math.min(index, 3));
+                    val = arg.substring(index + 1);
+                }
+                
+                if (opt.equalsIgnoreCase("len")) {
+                    options &= ~ArabicShaping.LENGTH_MASK;
+                    if (val.equalsIgnoreCase("gs")) {
+                        options |= ArabicShaping.LENGTH_GROW_SHRINK;
+                    } else if (val.equalsIgnoreCase("sn")) {
+                        options |= ArabicShaping.LENGTH_FIXED_SPACES_NEAR;
+                    } else if (val.equalsIgnoreCase("se")) {
+                        options |= ArabicShaping.LENGTH_FIXED_SPACES_AT_END;
+                    } else if (val.equalsIgnoreCase("sb")) {
+                        options |= ArabicShaping.LENGTH_FIXED_SPACES_AT_BEGINNING;
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("dir")) {
+                    options &= ~ArabicShaping.TEXT_DIRECTION_MASK;
+                    if (val.equalsIgnoreCase("log")) {
+                        options |= ArabicShaping.TEXT_DIRECTION_LOGICAL;
+                    } else if (val.equalsIgnoreCase("vis")) {
+                        options |= ArabicShaping.TEXT_DIRECTION_VISUAL_LTR;
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("let")) {
+                    options &= ~ArabicShaping.LETTERS_MASK;
+                    if (val.equalsIgnoreCase("no")) {
+                        options |= ArabicShaping.LETTERS_NOOP;
+                    } else if (val.equalsIgnoreCase("sh")) {
+                        options |= ArabicShaping.LETTERS_SHAPE;
+                    } else if (val.equalsIgnoreCase("un")) {
+                        options |= ArabicShaping.LETTERS_UNSHAPE;
+                        /* TASHKEEL unsupported
+                    } else if (val.equalsIgnoreCase("ta")) {
+                        options |= ArabicShaping.LETTERS_SHAPE_EXCEPT_TASHKEEL;
+                        */
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("dig")) {
+                    options &= ~ArabicShaping.DIGITS_MASK;
+                    if (val.equalsIgnoreCase("no")) {
+                        options |= ArabicShaping.DIGITS_NOOP;
+                    } else if (val.equalsIgnoreCase("ea")) {
+                        options |= ArabicShaping.DIGITS_EN2AN;
+                    } else if (val.equalsIgnoreCase("ae")) {
+                        options |= ArabicShaping.DIGITS_AN2EN;
+                    } else if (val.equalsIgnoreCase("lr")) {
+                        options |= ArabicShaping.DIGITS_EN2AN_INIT_LR;
+                    } else if (val.equalsIgnoreCase("al")) {
+                        options |= ArabicShaping.DIGITS_EN2AN_INIT_AL;
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("typ")) {
+                    options &= ~ArabicShaping.DIGIT_TYPE_MASK;
+                    if (val.equalsIgnoreCase("an")) {
+                        options |= ArabicShaping.DIGIT_TYPE_AN;
+                    } else if (val.equalsIgnoreCase("ex")) {
+                        options |= ArabicShaping.DIGIT_TYPE_AN_EXTENDED;
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("dst")) {
+                    try {
+                        ds = Integer.parseInt(val);
+                    }
+                    catch (Exception e) {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("dln")) {
+                    try {
+                        dl = Integer.parseInt(val);
+                    }
+                    catch (Exception e) {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("sst")) {
+                    try {
+                        ss = Integer.parseInt(val);
+                    }
+                    catch (Exception e) {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("sln")) {
+                    try {
+                        sl = Integer.parseInt(val);
+                    }
+                    catch (Exception e) {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("tes")) {
+                    if (val.equalsIgnoreCase("cp")) {
+                        testtype = COPY;
+                    } else if (val.equalsIgnoreCase("ip")) {
+                        testtype = INPLACE;
+                    } else if (val.equalsIgnoreCase("st")) {
+                        testtype = STRING;
+                    } else {
+                        throwValError(opt, val);
+                    }
+                } else if (opt.equalsIgnoreCase("help")) {
+                    System.out.println(usage);
+                } else {
+                    throwOptError(opt);
+                }
+            } else {
+                // assume text
+                text = parseText(arg);
+            }
+        }
+
+        if (sl < 0) {
+            sl = text.length() - ss;
+            System.out.println("sl defaulting to " + sl);
+        }
+        if (dl < 0) {
+            dl = 2 * sl;
+            System.out.println("dl defaulting to " + dl);
+        }
+
+        ArabicShaping shaper = new ArabicShaping(options);
+        System.out.println("shaper: " + shaper);
+
+        char[] src = text.toCharArray();
+        System.out.println(" input: '" + escapedText(src, ss, sl) + "'");
+        if (testtype != STRING) {
+            System.out.println("start: " + ss + " length: " + sl + " total length: " + src.length);
+        }
+
+        int result = -1;
+        char[] dest = null;
+
+        try {
+            switch (testtype) {
+            case COPY:
+                dest = new char[ds + dl];
+                result = shaper.shape(src, ss, sl, dest, ds, dl);
+                break;
+
+            case INPLACE:
+                shaper.shape(src, ss, sl);
+                ds = ss;
+                result = sl;
+                dest = src;
+                break;
+
+            case STRING:
+                dest = shaper.shape(text).toCharArray();
+                ds = 0;
+                result = dest.length;
+                break;
+            }
+
+            System.out.println("output: '" + escapedText(dest, ds, result) + "'");
+            System.out.println("length: " + result);
+            if (ds != 0 || result != dest.length) {
+                System.out.println("full output: '" + escapedText(dest, 0, dest.length) + "'");
+            }
+        }
+        catch (ArabicShapingException e) {
+            System.out.println("Caught ArabicShapingException");
+            System.out.println(e);
+        }
+        catch (Exception e) {
+            System.out.println("Caught Exception");
+            System.out.println(e);
+        }
+    }
+
+    private static void throwOptError(String opt) {
+        throwUsageError("unknown option: " + opt);
+    }
+
+    private static void throwValError(String opt, String val) {
+        throwUsageError("unknown value: " + val + " for option: " + opt);
+    }
+
+    private static void throwUsageError(String message) {
+        StringBuffer buf = new StringBuffer("*** usage error ***\n");
+        buf.append(message);
+        buf.append("\n");
+        buf.append(usage);
+        throw new Error(buf.toString());
+    }
+
+    private static final String usage = 
+        "Usage: [option]* [text]\n" +
+        "  where option is in the format '-opt[:val]'\n" +
+        "  options are:\n" +
+        "    -len:[gs|sn|se|sb]    (length: grow/shrink, spaces near, spaces end, spaces beginning)\n" +
+        "    -dir:[log|vis]        (direction: logical, visual)\n" +
+        //        "    -let:[no|sh|un|ta]    (letters: noop, shape, unshape, tashkeel)\n" +
+        "    -let:[no|sh|un]       (letters: noop, shape, unshape)\n" +
+        "    -dig:[no|ea|ae|lr|al] (digits: noop, en2an, an2en, en2an_lr, en2an_al)\n" +
+        "    -typ:[an|ex]          (digit type: arabic, arabic extended)\n" +
+        "    -dst:#                (dest start: [integer])\n" +
+        "    -dln:#                (dest length (max size): [integer])\n" +
+        "    -sst:#                (source start: [integer])\n" +
+        "    -sln:#                (source length: [integer])\n" +
+        "    -tes:[cp|ip|st]       (test type: copy, in place, string)\n" +
+        "    -help                 (print this help message)\n" +
+        "  text can contain unicode escape values in the format '\\uXXXX' only\n";
+        
+    private static String escapedText(char[] text, int start, int length) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = start, e = start + length; i < e; ++i) {
+            char ch = text[i];
+            if (ch < 0x20 || ch > 0x7e) {
+                buf.append("\\u");
+                if (ch < 0x1000) {
+                    buf.append('0');
+                }
+                if (ch < 0x100) {
+                    buf.append('0');
+                }
+                if (ch < 0x10) {
+                    buf.append('0');
+                }
+                buf.append(Integer.toHexString(ch));
+            } else {
+                buf.append(ch);
+            }
+        }
+        return buf.toString();
+    }
+
+    private static String parseText(String text) {
+        // process unicode escapes (only)
+        StringBuffer buf = new StringBuffer();
+        char[] chars = text.toCharArray();
+        for (int i = 0; i < chars.length; ++i) {
+            char ch = chars[i];
+            if (ch == '\\') {
+                if ((i < chars.length - 1) &&
+                    (chars[i+1] == 'u')) {
+                    int val = Integer.parseInt(text.substring(i+2, i+6), 16);
+                    buf.append((char)val);
+                    i += 5;
+                } else {
+                    buf.append('\\');
+                }
+            } else {
+                buf.append(ch);
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/src/com/ibm/icu/test/text/UCharacterCompare.java b/src/com/ibm/icu/test/text/UCharacterCompare.java
new file mode 100755
index 0000000..124108c
--- /dev/null
+++ b/src/com/ibm/icu/test/text/UCharacterCompare.java
@@ -0,0 +1,313 @@
+/**
+*******************************************************************************
+* Copyright (C) 1996-2001, International Business Machines Corporation and    *
+* others. All Rights Reserved.                                                *
+*******************************************************************************
+*
+* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/test/text/Attic/UCharacterCompare.java,v $ 
+* $Date: 2001/03/23 19:52:03 $ 
+* $Revision: 1.6 $
+*
+*******************************************************************************
+*/
+
+package com.ibm.icu.test.text;
+
+import com.ibm.text.UCharacter;
+import com.ibm.text.UCharacterCategory;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+* A class to compare the difference in methods between java.lang.Character and
+* UCharacter
+* @author Syn Wee Quek
+* @since oct 06 2000
+* @see com.ibm.text.UCharacter
+*/
+
+public final class UCharacterCompare
+{ 
+  // private variables ================================================
+  
+  private static Hashtable m_hashtable_ = new Hashtable();
+  
+  // public methods ======================================================
+  
+  /**
+  * Main testing method
+  */
+  public static void main(String arg[])
+  {
+    try
+    {
+      FileWriter f;
+      if (arg.length == 0)
+        f = new FileWriter("compare.txt");
+      else
+        f = new FileWriter(arg[0]);
+      PrintWriter p = new PrintWriter(f);
+      p.print("char  character name                                                           ");
+      p.println("method name               ucharacter character");
+      for (char i = Character.MIN_VALUE; i < Character.MAX_VALUE; i ++)
+      {
+        if (UCharacter.isDefined(i) != Character.isDefined(i))
+          trackDifference(p, i, "isDefined()", "" + UCharacter.isDefined(i), 
+                          "" + Character.isDefined(i));
+        else
+        {
+          if (UCharacter.digit(i, 10) != Character.digit(i, 10))
+            trackDifference(p, i, "digit()", "" + UCharacter.digit(i, 10),
+                            "" + Character.digit(i, 10));
+          if (UCharacter.getNumericValue(i) != Character.getNumericValue(i))
+            trackDifference(p, i, "getNumericValue()", 
+                            "" + UCharacter.getNumericValue(i),
+                            "" + Character.getNumericValue(i));
+          if (!compareType(UCharacter.getType(i), Character.getType(i)))
+            trackDifference(p, i, "getType()", "" + UCharacter.getType(i),
+                            "" + Character.getType(i));
+          if (UCharacter.isDigit(i) != Character.isDigit(i))
+            trackDifference(p, i, "isDigit()",
+                            "" + UCharacter.isDigit(i), 
+                            "" + Character.isDigit(i));
+          if (UCharacter.isISOControl(i) != Character.isISOControl(i))
+            trackDifference(p, i, "isISOControl()", 
+                            "" + UCharacter.isISOControl(i),
+                            "" + Character.isISOControl(i));
+          if (UCharacter.isLetter(i) != Character.isLetter(i))
+            trackDifference(p, i, "isLetter()", "" + UCharacter.isLetter(i),
+                            "" + Character.isLetter(i));
+          if (UCharacter.isLetterOrDigit(i) != Character.isLetterOrDigit(i))
+            trackDifference(p, i, "isLetterOrDigit()", 
+                            "" + UCharacter.isLetterOrDigit(i),
+                            "" + Character.isLetterOrDigit(i));
+          if (UCharacter.isLowerCase(i) != Character.isLowerCase(i))
+            trackDifference(p, i, "isLowerCase()", 
+                            "" + UCharacter.isLowerCase(i),
+                            "" + Character.isLowerCase(i));
+          if (UCharacter.isWhitespace(i) != Character.isWhitespace(i))
+            trackDifference(p, i, "isWhitespace()", 
+                            "" + UCharacter.isWhitespace(i),
+                            "" + Character.isWhitespace(i));
+          if (UCharacter.isSpaceChar(i) != Character.isSpaceChar(i))
+            trackDifference(p, i, "isSpaceChar()", 
+                            "" + UCharacter.isSpaceChar(i),
+                            "" + Character.isSpaceChar(i));
+          if (UCharacter.isTitleCase(i) != Character.isTitleCase(i))
+            trackDifference(p, i, "isTitleChar()", 
+                            "" + UCharacter.isTitleCase(i),
+                            "" + Character.isTitleCase(i));
+          if (UCharacter.isUnicodeIdentifierPart(i) != 
+              Character.isUnicodeIdentifierPart(i))
+            trackDifference(p, i, "isUnicodeIdentifierPart()", 
+                            "" + UCharacter.isUnicodeIdentifierPart(i),
+                            "" + Character.isUnicodeIdentifierPart(i));
+          if (UCharacter.isUnicodeIdentifierStart(i) != 
+              Character.isUnicodeIdentifierStart(i))
+            trackDifference(p, i, "isUnicodeIdentifierStart()",
+                            "" + UCharacter.isUnicodeIdentifierStart(i),
+                            "" + Character.isUnicodeIdentifierStart(i));
+          if (UCharacter.isIdentifierIgnorable(i) != 
+              Character.isIdentifierIgnorable(i))
+            trackDifference(p, i, "isIdentifierIgnorable()",
+                            "" + UCharacter.isIdentifierIgnorable(i),
+                            "" + Character.isIdentifierIgnorable(i));
+          if (UCharacter.isUpperCase(i) != Character.isUpperCase(i))
+            trackDifference(p, i, "isUpperCase()", 
+                            "" + UCharacter.isUpperCase(i),
+                            "" + Character.isUpperCase(i));
+          if (UCharacter.toLowerCase(i) != Character.toLowerCase(i))
+            trackDifference(p, i, "toLowerCase()", 
+                            Integer.toHexString(UCharacter.toLowerCase(i)),
+                            Integer.toHexString(Character.toLowerCase(i)));
+          if (!UCharacter.toString(i).equals(new Character(i).toString()))
+            trackDifference(p, i, "toString()",
+                            UCharacter.toString(i),
+                            new Character(i).toString());
+          if (UCharacter.toTitleCase(i) != Character.toTitleCase(i))
+            trackDifference(p, i, "toTitleCase()",
+                            Integer.toHexString(UCharacter.toTitleCase(i)),
+                            Integer.toHexString(Character.toTitleCase(i)));
+          if (UCharacter.toUpperCase(i) != Character.toUpperCase(i))
+            trackDifference(p, i, "toUpperCase()",
+                            Integer.toHexString(UCharacter.toUpperCase(i)),
+                            Integer.toHexString(Character.toUpperCase(i)));
+        }
+      }
+      summary(p);
+      p.close();
+    } 
+    catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+  
+  // private methods ===================================================
+  
+  /**
+  * Comparing types
+  * @param uchartype UCharacter type
+  * @param jchartype java.lang.Character type
+  */
+  private static boolean compareType(int uchartype, int jchartype)
+  {
+    if (uchartype == UCharacterCategory.UNASSIGNED && 
+        jchartype == Character.UNASSIGNED)
+      return true;
+    if (uchartype == UCharacterCategory.UPPERCASE_LETTER && 
+        jchartype == Character.UPPERCASE_LETTER)
+      return true;                                                   
+    if (uchartype == UCharacterCategory.LOWERCASE_LETTER && 
+        jchartype == Character.LOWERCASE_LETTER)
+      return true;                                                   
+    if (uchartype == UCharacterCategory.TITLECASE_LETTER && 
+        jchartype == Character.TITLECASE_LETTER)
+      return true;
+    if (uchartype == UCharacterCategory.MODIFIER_LETTER && 
+        jchartype == Character.MODIFIER_LETTER)
+      return true;
+    if (uchartype == UCharacterCategory.OTHER_LETTER && 
+        jchartype == Character.OTHER_LETTER)
+      return true;
+    if (uchartype == UCharacterCategory.NON_SPACING_MARK && 
+        jchartype == Character.NON_SPACING_MARK)
+      return true;
+    if (uchartype == UCharacterCategory.ENCLOSING_MARK && 
+        jchartype == Character.ENCLOSING_MARK)
+      return true;
+    if (uchartype == UCharacterCategory.COMBINING_SPACING_MARK && 
+        jchartype == Character.COMBINING_SPACING_MARK)
+      return true;
+	  if (uchartype == UCharacterCategory.DECIMAL_DIGIT_NUMBER && 
+        jchartype == Character.DECIMAL_DIGIT_NUMBER)
+      return true;
+	  if (uchartype == UCharacterCategory.LETTER_NUMBER && 
+        jchartype == Character.LETTER_NUMBER)
+      return true;
+	  if (uchartype == UCharacterCategory.OTHER_NUMBER && 
+        jchartype == Character.OTHER_NUMBER)
+      return true;
+	  if (uchartype == UCharacterCategory.SPACE_SEPARATOR && 
+        jchartype == Character.SPACE_SEPARATOR)
+      return true;
+	  if (uchartype == UCharacterCategory.LINE_SEPARATOR && 
+        jchartype == Character.LINE_SEPARATOR)
+      return true;
+	  if (uchartype == UCharacterCategory.PARAGRAPH_SEPARATOR && 
+        jchartype == Character.PARAGRAPH_SEPARATOR)
+      return true;
+	  if (uchartype == UCharacterCategory.CONTROL && 
+        jchartype == Character.CONTROL)
+      return true;
+    if (uchartype == UCharacterCategory.FORMAT && 
+        jchartype == Character.FORMAT)
+      return true;
+	  if (uchartype == UCharacterCategory.PRIVATE_USE && 
+        jchartype == Character.PRIVATE_USE)
+      return true;
+    if (uchartype == UCharacterCategory.SURROGATE && 
+        jchartype == Character.SURROGATE)
+      return true;
+    if (uchartype == UCharacterCategory.DASH_PUNCTUATION && 
+        jchartype == Character.DASH_PUNCTUATION)
+      return true;
+	  if (uchartype == UCharacterCategory.START_PUNCTUATION && 
+        jchartype == Character.START_PUNCTUATION)
+      return true;
+	  if (uchartype == UCharacterCategory.END_PUNCTUATION && 
+        jchartype == Character.END_PUNCTUATION)
+      return true;
+    if (uchartype == UCharacterCategory.CONNECTOR_PUNCTUATION && 
+        jchartype == Character.CONNECTOR_PUNCTUATION)
+      return true;
+    if (uchartype == UCharacterCategory.OTHER_PUNCTUATION && 
+        jchartype == Character.OTHER_PUNCTUATION)
+      return true;
+	  if (uchartype == UCharacterCategory.MATH_SYMBOL && 
+        jchartype == Character.MATH_SYMBOL)
+      return true;
+	  if (uchartype == UCharacterCategory.CURRENCY_SYMBOL && 
+        jchartype == Character.CURRENCY_SYMBOL)
+      return true;
+	  if (uchartype == UCharacterCategory.MODIFIER_SYMBOL && 
+        jchartype == Character.MODIFIER_SYMBOL)
+      return true;
+	  if (uchartype == UCharacterCategory.OTHER_SYMBOL && 
+        jchartype == Character.OTHER_SYMBOL)
+      return true;
+	  if (uchartype == UCharacterCategory.INITIAL_PUNCTUATION && 
+        jchartype == Character.START_PUNCTUATION)
+      return true;
+    if (uchartype == UCharacterCategory.FINAL_PUNCTUATION && 
+        jchartype == Character.END_PUNCTUATION)
+      return true;
+	  /*if (uchartype == UCharacterCategory.GENERAL_OTHER_TYPES && 
+        jchartype == Character.GENERAL_OTHER_TYPES)
+      return true;*/
+    return false;
+	}
+	
+	/**
+	* Difference writing to file
+	* @param f file outputstream
+	* @param ch code point
+	* @param method for testing
+	* @param ucharval UCharacter value after running method
+	* @param charval Character value after running method
+	* @exception thrown when error occur in writing to file
+	*/
+	private static void trackDifference(PrintWriter f, int ch, String method,
+	                                    String ucharval, String charval)
+	  throws Exception
+	{
+	  if (m_hashtable_.containsKey(method))
+	  {
+	    Integer value = (Integer)m_hashtable_.get(method);
+	    m_hashtable_.put(method, new Integer(value.intValue() + 1));
+	  }
+	  else
+	    m_hashtable_.put(method, new Integer(1));
+	  
+	  String temp = Integer.toHexString(ch);
+	  StringBuffer s = new StringBuffer(temp);
+	  for (int i = 0; i < 6 - temp.length(); i ++)
+	    s.append(' ');
+	  temp = UCharacter.getName(ch);
+	  if (temp == null)
+	    temp = " ";
+	  s.append(temp);
+	  for (int i = 0; i < 73 - temp.length(); i ++)
+	    s.append(' ');
+	  
+	  s.append(method);
+	  for (int i = 0; i < 27 - method.length(); i ++)
+	    s.append(' ');
+	  s.append(ucharval);
+	  for (int i = 0; i < 11 - ucharval.length(); i ++)
+	    s.append(' ');
+	  s.append(charval);
+	  f.println(s.toString());
+	}
+	
+	/**
+	* Does up a summary of the differences
+	* @param f file outputstream
+	*/
+	private static void summary(PrintWriter f)
+	{
+	  f.println("==================================================");
+	  f.println("Summary of differences");
+	  for (Enumeration e = m_hashtable_.keys() ; e.hasMoreElements() ;) 
+	  {
+	    StringBuffer method = new StringBuffer((String)e.nextElement());
+	    int count = ((Integer)m_hashtable_.get(method.toString())).intValue();
+	    for (int i = 30 - method.length(); i > 0; i --)
+	      method.append(' ');
+      f.println(method + "  " + count);
+    }
+	}
+}
+
diff --git a/src/com/ibm/icu/test/text/UCharacterTest.java b/src/com/ibm/icu/test/text/UCharacterTest.java
new file mode 100755
index 0000000..fc647a5
--- /dev/null
+++ b/src/com/ibm/icu/test/text/UCharacterTest.java
@@ -0,0 +1,678 @@
+/**
+*******************************************************************************
+* Copyright (C) 1996-2001, International Business Machines Corporation and    *
+* others. All Rights Reserved.                                                *
+*******************************************************************************
+*
+* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/test/text/Attic/UCharacterTest.java,v $ 
+* $Date: 2001/03/23 19:52:03 $ 
+* $Revision: 1.9 $
+*
+*******************************************************************************
+*/
+
+package com.ibm.icu.test.text;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+import java.util.Locale;
+import com.ibm.test.TestFmwk;
+import com.ibm.text.UCharacter;
+import com.ibm.text.UCharacterCategory;
+import com.ibm.text.UCharacterDirection;
+import com.ibm.text.UTF16;
+
+/**
+* Testing class for UCharacter
+* Mostly following the test cases for ICU
+* @author Syn Wee Quek
+* @since nov 04 2000
+*/
+public final class UCharacterTest extends TestFmwk
+{ 
+  // private variables =============================================
+  
+  /**
+  * ICU4J data version number
+  */
+  private final String VERSION_ = "3.0.0.0";
+  
+  // constructor ===================================================
+  
+  /**
+  * Constructor
+  */
+  public UCharacterTest()
+  {
+  }
+  
+  // public methods ================================================
+  
+  /**
+  * Testing the uppercase and lowercase function of UCharacter
+  */
+  public void TestUpperLower()
+  {
+    // variables to test the uppercase and lowercase characters
+    int upper[] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0xb1, 0xb2, 
+                   0xb3, 0x48, 0x49, 0x4a, 0x2e, 0x3f, 0x3a, 0x4b, 0x4c,
+                   0x4d, 0x4e, 0x4f, 0x01c4, 0x01c8, 0x000c, 0x0000};
+    int lower[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0xb1, 0x00b2, 
+                   0xb3, 0x68, 0x69, 0x6a, 0x2e, 0x3f, 0x3a, 0x6b, 0x6c,
+                   0x6d, 0x6e, 0x6f, 0x01c6, 0x01c9, 0x000c, 0x0000};
+    
+    int size = upper.length;
+    
+    for (int i = 0; i < size; i ++) 
+    {
+      if (UCharacter.isLetter(lower[i]) && !UCharacter.isLowerCase(lower[i]))
+      {
+        errln("FAIL isLowerCase test for 0x" + 
+              Integer.toHexString(lower[i]));
+        break;
+      }
+      if (UCharacter.isLetter(upper[i]) && !(UCharacter.isUpperCase(upper[i]) 
+                                       || UCharacter.isTitleCase(upper[i])))     
+      {
+        errln("FAIL isUpperCase test for 0x" + 
+              Integer.toHexString(upper[i]));
+        break;
+      }
+      if (lower[i] != UCharacter.toLowerCase(upper[i]) || 
+          (upper[i] != UCharacter.toUpperCase(lower[i]) &&
+            upper[i] != UCharacter.toTitleCase(lower[i])))
+      {
+        errln("FAIL case conversion test for 0x" + 
+              Integer.toHexString(upper[i]) + " to 0x" + 
+              Integer.toHexString(lower[i]));
+        break;
+      }
+      if (lower[i] != UCharacter.toLowerCase(lower[i]))
+      {
+        errln("FAIL lower case conversion test for 0x" + 
+        Integer.toHexString(lower[i]));
+        break;
+      }
+      if (upper[i] != UCharacter.toUpperCase(upper[i]) && 
+          upper[i] != UCharacter.toTitleCase(upper[i]))
+      {
+        errln("FAIL upper case conversion test for 0x" + 
+              Integer.toHexString(upper[i]));
+        break;
+      }
+      logln("Ok    0x" + Integer.toHexString(upper[i]) + " and 0x" +
+            Integer.toHexString(lower[i]));
+    }
+  }
+  
+  /**
+  * Testing the letter and number determination in UCharacter
+  */
+  public void TestLetterNumber()
+  {
+    for (int i = 0x0041; i < 0x005B; i ++) 
+      if (!UCharacter.isLetter(i))
+        errln("FAIL 0x" + Integer.toHexString(i) + " expected to be a letter");
+        
+    for (int i = 0x0660; i < 0x066A; i ++) 
+      if (UCharacter.isLetter(i))
+        errln("FAIL 0x" + Integer.toHexString(i) + 
+              " expected not to be a letter");
+    
+    for (int i = 0x0660; i < 0x066A; i ++) 
+      if (!UCharacter.isDigit(i))
+        errln("FAIL 0x" + Integer.toHexString(i) + " expected to be a digit");
+    
+    for (int i = 0x0041; i < 0x005B; i ++) 
+      if (!UCharacter.isLetterOrDigit(i))
+        errln("FAIL 0x" + Integer.toHexString(i) + 
+              " expected not to be a digit");
+        
+    for (int i = 0x0660; i < 0x066A; i ++) 
+      if (!UCharacter.isLetterOrDigit(i))
+        errln("FAIL 0x" + Integer.toHexString(i) + 
+              "expected to be either a letter or a digit");
+  }
+
+  /**
+  * Tests for space determination in UCharacter
+  */
+  public void TestSpaces()
+  {
+    int spaces[] = {0x0020, 0x00a0, 0x2000, 0x2001, 0x2005};
+    int nonspaces[] = {0x61, 0x62, 0x63, 0x64, 0x74};
+    int whitespaces[] = {0x2008, 0x2009, 0x200a, 0x001c, 0x000c};
+    int nonwhitespaces[] = {0x61, 0x62, 0x3c, 0x28, 0x3f};
+                       
+    int size = spaces.length;
+    for (int i = 0; i < size; i ++)
+    {
+      if (!UCharacter.isSpaceChar(spaces[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(spaces[i]) + 
+              " expected to be a space character");
+        break;
+      }
+      
+      if (UCharacter.isSpaceChar(nonspaces[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonspaces[i]) + 
+              " expected not to be space character");
+        break;
+      }
+ 
+      if (!UCharacter.isWhitespace(whitespaces[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(whitespaces[i]) + 
+              " expected to be a white space character");
+        break;
+      }
+      if (UCharacter.isWhitespace(nonwhitespaces[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonwhitespaces[i]) + 
+              " expected not to be a space character");
+        break;
+      }
+      logln("Ok    0x" + Integer.toHexString(spaces[i]) + " and 0x" +
+            Integer.toHexString(nonspaces[i]) + " and 0x" +
+            Integer.toHexString(whitespaces[i]) + " and 0x" +
+            Integer.toHexString(nonwhitespaces[i]));
+    }
+  }
+  
+  /**
+  * Tests for defined and undefined characters
+  */
+  public void TestDefined()
+  {
+    int undefined[] = {0xfff1, 0xfff7, 0xfa30};
+    int defined[] = {0x523E, 0x4f88, 0xfffd};
+    
+    int size = undefined.length;
+    for (int i = 0; i < size; i ++) 
+    {
+      if (UCharacter.isDefined(undefined[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(undefined[i]) + 
+              " expected not to be defined");
+        break;
+      }
+      if (!UCharacter.isDefined(defined[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(defined[i]) + 
+              " expected defined");
+        break;
+      }
+    }
+  }
+  
+  /**
+  * Tests for base characters and their cellwidth
+  */
+  public void TestBase()
+  {
+    int base[] = {0x0061, 0x0031, 0x03d2};
+    int nonbase[] = {0x002B, 0x0020, 0x203B};    
+    int size = base.length;
+    for (int i = 0; i < size; i ++) 
+    {
+      if (UCharacter.isBaseForm(nonbase[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonbase[i]) + 
+              " expected not to be a base character");
+        break;
+      }
+      if (!UCharacter.isBaseForm(base[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(base[i]) + 
+              " expected to be a base character");
+        break;
+      }
+    }
+  }
+    
+  /**
+  * Tests for digit characters 
+  */
+  public void TestDigits()
+  {
+    int digits[] = {0x0030, 0x0662, 0x0F23, 0x0ED5, 0x2160};
+    
+    //special characters not in the properties table
+    int digits2[] = {0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56d8, 0x4e94, 0x516d, 
+                     0x4e03, 0x516b, 0x4e5d}; 
+    int nondigits[] = {0x0010, 0x0041, 0x0122, 0x68FE};
+    
+    int digitvalues[] = {0, 2, 3, 5, 1};
+    int digitvalues2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};     
+    
+    int size  = digits.length;
+    for (int i = 0; i < size; i ++) 
+      if (UCharacter.isDigit(digits[i]) && 
+          UCharacter.digit(digits[i]) != digitvalues[i]) 
+      {
+        errln("FAIL 0x" + Integer.toHexString(digits[i]) + 
+              " expected digit with value " + digitvalues[i]);
+        break;
+      }
+    
+    size = nondigits.length;
+    for (int i = 0; i < size; i ++)
+      if (UCharacter.isDigit(nondigits[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nondigits[i]) + 
+              " expected nondigit");
+        break;
+      }
+      
+    size = digits2.length;
+    for (int i = 0; i < 10; i ++) 
+      if (UCharacter.isDigit(digits2[i]) &&
+          UCharacter.digit(digits2[i]) != digitvalues2[i]) 
+      {
+        errln("FAIL 0x" + Integer.toHexString(digits2[i]) + 
+              " expected digit with value " + digitvalues2[i]);
+        break;
+      }
+  }
+
+  /**
+  * Tests for version 
+  */
+  public void TestVersion()
+  {
+    String version = UCharacter.getUnicodeVersion();
+    if (!version.equals(VERSION_))
+      errln("FAIL expected " + VERSION_);
+  }
+  
+  /**
+  * Tests for control characters
+  */
+  /*
+  public void TestControl()
+  {
+    int control[] = {0x001b, 0x0097, 0x0082};
+    int noncontrol[] = {0x61, 0x0031, 0x00e2};
+    
+    int size = control.length;
+    for (int i = 0; i < size; i ++) 
+    {
+      if (!UCharacter.isControl(control[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(control[i]) + 
+              " expected to be a control character");
+        break;
+      }  
+      if (UCharacter.isControl(noncontrol[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(noncontrol[i]) + 
+              " expected to be not a control character");
+        break;
+      }
+      
+      logln("Ok    0x" + Integer.toHexString(control[i]) + " and 0x" +
+            Integer.toHexString(noncontrol[i]));
+    }
+  }
+  */
+  
+  /**
+  * Tests for printable characters
+  */
+  public void TestPrint()
+  {
+    int printable[] = {0x0042, 0x005f, 0x2014};
+    int nonprintable[] = {0x200c, 0x009f, 0x001b};
+    
+    int size = printable.length;
+    for (int i = 0; i < size; i ++)
+    {
+      if (!UCharacter.isPrintable(printable[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(printable[i]) + 
+              " expected to be a printable character");
+        break;
+      }
+      if (UCharacter.isPrintable(nonprintable[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonprintable[i]) +
+              " expected not to be a printable character");
+        break;
+      }
+      logln("Ok    0x" + Integer.toHexString(printable[i]) + " and 0x" +
+            Integer.toHexString(nonprintable[i]));
+    }
+    
+    // test all ISO 8 controls
+    for (int ch = 0; ch <= 0x9f; ++ ch) {
+      if (ch == 0x20) {
+      // skip ASCII graphic characters and continue with DEL
+        ch = 0x7f;
+      }
+      if (UCharacter.isPrintable(ch)) {
+        errln("Fail 0x" + Integer.toHexString(ch) + 
+              " is a ISO 8 control character hence not printable\n");
+      }
+    }
+
+    /* test all Latin-1 graphic characters */
+    for (int ch = 0x20; ch <= 0xff; ++ ch) {
+      if (ch == 0x7f) {
+        ch = 0xa0;
+      }
+      if (!UCharacter.isPrintable(ch)) {
+        errln("Fail 0x" + Integer.toHexString(ch) + 
+              " is a Latin-1 graphic character\n");
+        }
+    }
+  }
+  
+  /** 
+  * Testing for identifier characters
+  */
+  public void TestIdentifier()
+  {
+    int unicodeidstart[] = {0x0250, 0x00e2, 0x0061};
+    int nonunicodeidstart[] = {0x2000, 0x000a, 0x2019};
+    int unicodeidpart[] = {0x005f, 0x0032, 0x0045};
+    int nonunicodeidpart[] = {0x2030, 0x00a3, 0x0020};
+    int idignore[] = {0x070F, 0x180B, 0x180C};
+    int nonidignore[] = {0x0075, 0x00a3, 0x0061};
+
+    int size = unicodeidstart.length;
+    for (int i = 0; i < size; i ++) 
+    {
+      if (!UCharacter.isUnicodeIdentifierStart(unicodeidstart[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(unicodeidstart[i]) + 
+              " expected to be a unicode identifier start character");
+        break;
+      }
+      if (UCharacter.isUnicodeIdentifierStart(nonunicodeidstart[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonunicodeidstart[i]) + 
+              " expected not to be a unicode identifier start character");
+        break;
+      }
+      if (!UCharacter.isUnicodeIdentifierPart(unicodeidpart[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(unicodeidpart[i]) + 
+              " expected to be a unicode identifier part character");
+        break;
+      }
+      if (UCharacter.isUnicodeIdentifierPart(nonunicodeidpart[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonunicodeidpart[i]) + 
+              " expected not to be a unicode identifier part character");
+        break;
+      }
+      if (!UCharacter.isIdentifierIgnorable(idignore[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(idignore[i]) + 
+              " expected to be a ignorable unicode character");
+        break;
+      }
+      if (UCharacter.isIdentifierIgnorable(nonidignore[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(nonidignore[i]) + 
+              " expected not to be a ignorable unicode character");
+        break;
+      }
+      logln("Ok    0x" + Integer.toHexString(unicodeidstart[i]) + " and 0x" +
+            Integer.toHexString(nonunicodeidstart[i]) + " and 0x" +
+            Integer.toHexString(unicodeidpart[i]) + " and 0x" +
+            Integer.toHexString(nonunicodeidpart[i]) + " and 0x" +
+            Integer.toHexString(idignore[i]) + " and 0x" +
+            Integer.toHexString(nonidignore[i]));
+    }
+  }
+  
+  /**
+  * Tests for the character types, direction.<br>
+  * This method reads in UnicodeData.txt file for testing purposes. A default 
+  * path is provided relative to the class path, however if the user could 
+  * set a system property to change the path.<br>
+  * e.g. java -DUnicodeData="anyfile.dat" com.ibm.test.text.UCharacterTest
+  */
+  public void TestUnicodeData()
+  {
+    // this is the 2 char category types used in the UnicodeData file
+    final String TYPE = 
+      "LuLlLtLmLoMnMeMcNdNlNoZsZlZpCcCfCoCsPdPsPePcPoSmScSkSoPiPf";
+    
+    // directory types used in the UnicodeData file
+    // padded by spaces to make each type size 4
+    final String DIR = 
+      "L   R   EN  ES  ET  AN  CS  B   S   WS  ON  LRE LRO AL  RLE RLO PDF NSM BN  ";
+      
+    // default unicode data file name
+    final String UNICODE_DATA_FILE = "src//data//unicode//UnicodeData.txt";
+    
+    // unicode data file path system name
+    final String UNICODE_DATA_SYSTEM_NAME = "UnicodeData";
+    String s = System.getProperty(UNICODE_DATA_SYSTEM_NAME);
+    if (s == null)
+    // assuming runtime directory is on the same level as the source
+      s = System.getProperty("user.dir") + "//..//" + UNICODE_DATA_FILE;
+    
+    final int LASTUNICODECHAR = 0xFFFD;
+    int ch = 0,
+        index = 0,
+        type = 0,
+        dir = 0;
+	
+	  try
+	  {
+	    // reading in the UnicodeData file
+	    FileReader fr = new FileReader(s);
+	    BufferedReader input = new BufferedReader(fr);
+	    
+      while (ch != LASTUNICODECHAR)
+      {
+        s= input.readLine();
+        
+        // geting the unicode character, its type and its direction
+        ch = Integer.parseInt(s.substring(0, 4), 16);
+        index = s.indexOf(';', 5);
+        String t = s.substring(index + 1, index + 3);
+        index += 4;
+        byte cc = (byte)(Integer.parseInt(s.substring(index, 
+                                                      s.indexOf(';', index))));
+        index = s.indexOf(';', index);
+        String d = s.substring(index + 1, s.indexOf(';', index + 1));
+        
+        // testing the category
+        // we override the general category of some control characters
+        if (ch == 9 || ch == 0xb || ch == 0x1f)
+          type = UCharacterCategory.SPACE_SEPARATOR;
+        else
+          if (ch == 0xc)
+            type = UCharacterCategory.LINE_SEPARATOR;
+          else
+            if (ch == 0xa || ch == 0xd || ch == 0x1c || ch == 0x1d || 
+                ch == 0x1e || ch == 0x85)
+               type = UCharacterCategory.PARAGRAPH_SEPARATOR;
+            else
+            {
+              type = TYPE.indexOf(t);
+              if (type < 0)
+                type = 0;
+              else 
+                type = (type >> 1) + 1;  
+            }
+            
+        if (UCharacter.getType(ch) != type)
+        {
+          errln("FAIL 0x" + Integer.toHexString(ch) + " expected type " + 
+                type);
+          break;
+        }
+        
+        // testing combining class
+        if (UCharacter.getCombiningClass(ch) != cc)
+        {
+          errln("FAIL 0x" + Integer.toHexString(ch) + " expected combining " +
+                "class " + cc);
+          break;
+        }
+        
+        // testing the direction
+        if (d.length() == 1)
+          d = d + "   ";  
+          
+        dir = DIR.indexOf(d) >> 2;
+        if (UCharacter.getDirection(ch) != dir) 
+        {
+          errln("FAIL 0x" + Integer.toHexString(ch) + 
+                " expected wrong direction " + dir);
+          break;
+        }
+      }
+      input.close();
+    }
+    catch (FileNotFoundException e)
+    {
+      errln("FAIL UnicodeData.txt not found\n" +
+            "Configure the system setting UnicodeData to the right path\n" +
+            "e.g. java -DUnicodeData=\"anyfile.dat\" " +
+            "com.ibm.icu.test.text.UCharacterTest");
+    }
+    catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    
+    if (UCharacter.getDirection(0x10001) != 
+                                         UCharacterDirection.LEFT_TO_RIGHT) 
+      errln("FAIL 0x10001 expected direction " + 
+      UCharacterDirection.toString(UCharacterDirection.LEFT_TO_RIGHT));
+  }
+  
+  /**
+  * Test for the character names
+  */
+  public void TestNames()
+  {
+    int c[] = {0x0061, 0x0284, 0x3401, 0x7fed, 0xac00, 0xd7a3, 0xff08, 0xffe5};
+    String name[] = {"LATIN SMALL LETTER A", 
+                     "LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK", 
+                     "CJK UNIFIED IDEOGRAPH-3401", 
+                     "CJK UNIFIED IDEOGRAPH-7FED", "HANGUL SYLLABLE GA", 
+                     "HANGUL SYLLABLE HIH", "FULLWIDTH LEFT PARENTHESIS",
+                     "FULLWIDTH YEN SIGN"};
+    String oldname[] = {"", "LATIN SMALL LETTER DOTLESS J BAR HOOK", "", "",
+                        "", "", "FULLWIDTH OPENING PARENTHESIS", ""};
+    int size = c.length;
+    String str;
+    int uc;
+    
+    for (int i = 0; i < size; i ++) 
+    {
+      // modern Unicode character name
+      str = UCharacter.getName(c[i]);
+      if (!str.equalsIgnoreCase(name[i]))
+      {
+        errln("FAIL 0x" + Integer.toHexString(c[i]) + " expected name " +
+              name[i]);
+        break;
+      }
+     
+      // 1.0 Unicode character name
+      str = UCharacter.getName1_0(c[i]);
+      if ((str == null && oldname[i].length() > 0) || 
+          (str != null && !str.equalsIgnoreCase(oldname[i])))
+      {
+        errln("FAIL 0x" + Integer.toHexString(c[i]) + " expected 1.0 name " +
+              oldname[i]);
+        break;
+      }
+      
+      // retrieving unicode character from modern name
+      uc = UCharacter.getCharFromName(name[i]);
+      if (uc != c[i])
+      {
+        errln("FAIL " + name[i] + " expected character 0x" + 
+              Integer.toHexString(c[i]));
+        break;
+      }
+      
+      //retrieving unicode character from 1.0 name
+      uc = UCharacter.getCharFromName1_0(oldname[i]);
+      if (uc != c[i] && i != 0 && (i == 1 || i == 6))
+      {
+        errln("FAIL " + name[i] + " expected 1.0 character " + 
+              Integer.toHexString(c[i]));
+        break;
+      }
+    }
+    
+    // extra testing different from icu
+    for (int i = UCharacter.MIN_VALUE; i < UCharacter.MAX_VALUE; i ++)
+    {
+      str = UCharacter.getName(i);
+      if (str != null && UCharacter.getCharFromName(str) != i)
+      {
+        errln("FAIL 0x" + Integer.toHexString(i) + " " + str  + 
+              " retrieval of name and vice versa" );
+        break;
+      }
+    }
+  }
+  
+  /**
+  * Testing the strings case mapping methods
+  */
+  public void TestCaseMapping() 
+  {
+    String beforeLower =  "\u0061\u0042\u0049\u03a3\u00df\u03a3\u002f\ud93f\udfff",
+           lowerRoot =    "\u0061\u0062\u0069\u03c3\u00df\u03c2\u002f\ud93f\udfff",
+           lowerTurkish = "\u0061\u0062\u0131\u03c3\u00df\u03c2\u002f\ud93f\udfff",
+           beforeUpper =  "\u0061\u0042\u0069\u03c2\u00df\u03c3\u002f\ufb03\ud93f\udfff",
+           upperRoot =    "\u0041\u0042\u0049\u03a3\u0053\u0053\u03a3\u002f\u0046\u0046\u0049\ud93f\udfff",
+           upperTurkish = "\u0041\u0042\u0130\u03a3\u0053\u0053\u03a3\u002f\u0046\u0046\u0049\ud93f\udfff";
+
+    String result = UCharacter.toLowerCase(beforeLower);
+    if (!lowerRoot.equals(result)) 
+      errln("Fail " + beforeLower + " after lowercase should be " + lowerRoot);
+   
+    // lowercase with turkish locale
+    result = UCharacter.toLowerCase(new Locale("tr", "TR"), beforeLower);
+    if (!lowerTurkish.equals(result)) 
+      errln("Fail " + beforeLower + " after turkish-sensitive lowercase " +
+            "should be " + lowerRoot);
+            
+    // uppercase with root locale and in the same buffer
+    result = UCharacter.toUpperCase(beforeUpper);
+    if (!upperRoot.equals(result)) 
+      errln("Fail " + beforeUpper + " after uppercase should be " + upperRoot);
+      
+    // uppercase with turkish locale and separate buffers
+    result = UCharacter.toUpperCase(new Locale("tr", "TR"), beforeUpper);
+    if (!upperTurkish.equals(result)) 
+      errln("Fail " + beforeUpper + " after turkish-sensitive uppercase " +
+            "should be " + upperTurkish);
+            
+    // test preflighting
+    result = UCharacter.toLowerCase(beforeLower);
+    if (!lowerRoot.equals(result)) 
+      errln("Fail " + beforeLower + " after lower case should be " + 
+            lowerRoot);
+  }
+
+ 
+  public static void main(String[] arg)
+  {
+    try
+    {
+      UCharacterTest test = new UCharacterTest();
+      test.run(arg);
+    }
+    catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+}
+
diff --git a/src/com/ibm/icu/test/text/UTF16Test.java b/src/com/ibm/icu/test/text/UTF16Test.java
new file mode 100755
index 0000000..372b7c6
--- /dev/null
+++ b/src/com/ibm/icu/test/text/UTF16Test.java
@@ -0,0 +1,226 @@
+/**
+*******************************************************************************
+* Copyright (C) 1996-2001, International Business Machines Corporation and    *
+* others. All Rights Reserved.                                                *
+*******************************************************************************
+*
+* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/test/text/Attic/UTF16Test.java,v $ 
+* $Date: 2001/03/23 19:52:03 $ 
+* $Revision: 1.5 $
+*
+*******************************************************************************
+*/
+
+package com.ibm.icu.test.text;
+
+import com.ibm.test.TestFmwk;
+import com.ibm.text.UCharacter;
+import com.ibm.text.UTF16;
+
+/**
+* Testing class for UTF16
+* @author Syn Wee Quek
+* @since feb 09 2001
+*/
+public final class UTF16Test extends TestFmwk
+{ 
+  // constructor ===================================================
+  
+  /**
+  * Constructor
+  */
+  public UTF16Test()
+  {
+  }
+  
+  // public methods ================================================
+  
+  /**
+  * Testing UTF16 class methods append, getCharCount and bounds
+  */
+  public void TestUTF16AppendBoundCount()
+  {
+    StringBuffer str = new StringBuffer("this is a string ");
+    int initstrsize = str.length();
+    int length;
+    
+    for (int i = UCharacter.MIN_VALUE; i < UCharacter.MAX_VALUE; i += 100)
+    {
+      length = str.length();
+      UTF16.append(str, i);
+      
+      // this is to cater for the combination of 0xDBXX 0xDC50 which forms
+      // a supplementary character
+      if (i == 0xDC50)
+        initstrsize --;
+        
+      if (UTF16.countCodePoint(str.toString()) != initstrsize + (i / 100) + 1)
+      {
+        errln("FAIL Counting code points in string appended with " + 
+              " 0x" + Integer.toHexString(i));
+        break;
+      }
+       
+      if (!UCharacter.isSupplementary(i))
+      {
+        if (UTF16.getCharCount(i) != 1)
+        {
+          errln("FAIL Counting BMP character size error" );
+          break;
+        }  
+        if (str.length() != length + 1)
+        {
+          errln("FAIL Adding a BMP character error" );
+          break;
+        }
+        if (!UTF16.isSurrogate((char)i) && 
+            (UTF16.bounds(str.toString(), str.length() - 1) != 
+                                                UTF16.SINGLE_CHAR_BOUNDARY ||
+             UTF16.boundsAtCodePointOffset(str.toString(), 
+                                           initstrsize + (i /100)) 
+                                              != UTF16.SINGLE_CHAR_BOUNDARY))
+        {
+          errln("FAIL Finding BMP character bounds error" );
+          break;
+        }
+      }
+      else 
+      {
+        if (UTF16.getCharCount(i) != 2)
+        {
+          errln("FAIL Counting Supplementary character size error" );
+          break;
+        }
+        if (str.length() != length + 2)
+        {
+          errln("FAIL Adding a Supplementary character error" );
+          break;
+        }
+        length = str.length();
+        if (UTF16.bounds(str.toString(), str.length() - 2) != 
+            UTF16.LEAD_SURROGATE_BOUNDARY || 
+            UTF16.bounds(str.toString(), str.length() - 1) != 
+            UTF16.TRAIL_SURROGATE_BOUNDARY ||
+            UTF16.boundsAtCodePointOffset(str.toString(), 
+                                          initstrsize + (i / 100)) 
+                                            != UTF16.LEAD_SURROGATE_BOUNDARY)
+        {
+          errln("FAIL Finding Supplementary character bounds error with " +
+                "string appended with 0x" + Integer.toHexString(i));
+          break;
+        }
+      }
+    } 
+  }
+  
+  /**
+  * Testing UTF16 class methods findCodePointOffset, findOffsetFromCodePoint, charAt and
+  * charAtCodePoint 
+  */
+  public void TestUTF16OffsetCharAt()
+  {
+    StringBuffer str = new StringBuffer("12345");
+    UTF16.append(str, 0x10001);
+    str.append("67890");
+    UTF16.append(str, 0x10002);
+    String s = str.toString();
+    if (UTF16.charAt(s, 0) != '1' || UTF16.charAt(s, 2) != '3' || 
+        UTF16.charAt(s, 5) != 0x10001 || UTF16.charAt(s, 6) != 0x10001 || 
+        UTF16.charAt(s, 12) != 0x10002 || UTF16.charAt(s, 13) != 0x10002 ||
+        UTF16.charAtCodePointOffset(s, 0) != '1' || 
+        UTF16.charAtCodePointOffset(s, 2) != '3' || 
+        UTF16.charAtCodePointOffset(s, 5) != 0x10001 || 
+        UTF16.charAtCodePointOffset(s, 6) != '6' || 
+        UTF16.charAtCodePointOffset(s, 11) != 0x10002)
+      errln("FAIL Getting character from string error" );
+
+    if (UTF16.findCodePointOffset(s, 3) != 3 || 
+        UTF16.findCodePointOffset(s, 5) != 5 || 
+        UTF16.findCodePointOffset(s, 6) != 5)
+      errln("FAIL Getting codepoint offset from string error" );
+    if (UTF16.findOffsetFromCodePoint(s, 3) != 3 || 
+        UTF16.findOffsetFromCodePoint(s, 5) != 5 || 
+        UTF16.findOffsetFromCodePoint(s, 6) != 7)
+      errln("FAIL Getting UTF16 offset from codepoint in string error" );
+      
+    UTF16.setCharAt(str, 3, '3');
+    UTF16.setCharAtCodePointOffset(str, 4, '3');
+    if (UTF16.charAt(str.toString(), 3) != '3' || 
+        UTF16.charAtCodePointOffset(str.toString(), 3) != '3' ||
+        UTF16.charAt(str.toString(), 4) != '3' || 
+        UTF16.charAtCodePointOffset(str.toString(), 4) != '3')
+      errln("FAIL Setting non-supplementary characters at a " +
+            "non-supplementary position");
+            
+    UTF16.setCharAt(str, 5, '3');
+    if (UTF16.charAt(str.toString(), 5) != '3' || 
+        UTF16.charAtCodePointOffset(str.toString(), 5) != '3' || 
+        UTF16.charAt(str.toString(), 6) != '6' || 
+        UTF16.charAtCodePointOffset(str.toString(), 5) != '3' || 
+        UTF16.charAtCodePointOffset(str.toString(), 6) != '6')
+      errln("FAIL Setting non-supplementary characters at a " +
+            "supplementary position");
+            
+    UTF16.setCharAt(str, 5, 0x10001);
+    if (UTF16.charAt(str.toString(), 5) != 0x10001 || 
+        UTF16.charAtCodePointOffset(str.toString(), 5) != 0x10001 ||
+        UTF16.charAt(str.toString(), 7) != '6' || 
+        UTF16.charAtCodePointOffset(str.toString(), 6) != '6')
+      errln("FAIL Setting supplementary characters at a " +
+            "non-supplementary position");
+            
+    UTF16.setCharAtCodePointOffset(str, 5, '3');
+    if (UTF16.charAt(str.toString(), 5) != '3' || 
+        UTF16.charAtCodePointOffset(str.toString(), 5) != '3' ||
+        UTF16.charAt(str.toString(), 6) != '6' || 
+        UTF16.charAtCodePointOffset(str.toString(), 6) != '6')
+      errln("FAIL Setting non-supplementary characters at a " +
+            "supplementary position");
+            
+    UTF16.setCharAt(str, 5, 0x10001);
+    if (UTF16.charAt(str.toString(), 5) != 0x10001 || 
+        UTF16.charAtCodePointOffset(str.toString(), 5) != 0x10001 ||
+        UTF16.charAt(str.toString(), 7) != '6' || 
+        UTF16.charAtCodePointOffset(str.toString(), 6) != '6')
+      errln("FAIL Setting supplementary characters at a " +
+            "non-supplementary position");
+            
+     
+   UTF16.setCharAt(str, 5, 0xD800);
+   UTF16.setCharAt(str, 6, 0xD800);
+   if (UTF16.charAt(str.toString(), 5) != 0xD800 ||
+       UTF16.charAt(str.toString(), 6) != 0xD800 ||
+       UTF16.charAtCodePointOffset(str.toString(), 5) != 0xD800 ||
+       UTF16.charAtCodePointOffset(str.toString(), 6) != 0xD800)
+      errln("FAIL Setting lead characters at a supplementary position");   
+      
+   UTF16.setCharAt(str, 5, 0xDDDD);
+   if (UTF16.charAt(str.toString(), 5) != 0xDDDD ||
+       UTF16.charAt(str.toString(), 6) != 0xD800 ||
+       UTF16.charAtCodePointOffset(str.toString(), 5) != 0xDDDD ||
+       UTF16.charAtCodePointOffset(str.toString(), 6) != 0xD800)
+      errln("FAIL Setting trail characters at a surrogate position");
+      
+   UTF16.setCharAt(str, 5, '3');
+   if (UTF16.charAt(str.toString(), 5) != '3' ||
+       UTF16.charAt(str.toString(), 6) != 0xD800 ||
+       UTF16.charAtCodePointOffset(str.toString(), 5) != '3' ||
+       UTF16.charAtCodePointOffset(str.toString(), 6) != 0xD800)
+      errln("FAIL Setting non-supplementary characters at a surrogate " +
+            "position");
+  }
+ 
+  public static void main(String[] arg)
+  {
+    try
+    {
+      UTF16Test test = new UTF16Test();
+      test.run(arg);
+    }
+    catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+}
+
diff --git a/src/com/ibm/math/BigDecimal.java b/src/com/ibm/math/BigDecimal.java
new file mode 100755
index 0000000..df92e5a
--- /dev/null
+++ b/src/com/ibm/math/BigDecimal.java
@@ -0,0 +1,4358 @@
+/* Generated from 'BigDecimal.nrx' 8 Sep 2000 11:10:50 [v2.00] */
+/* Options: Binary Comments Crossref Format Java Logo Strictargs Strictcase Trace2 Verbose3 */
+package com.ibm.math;
+import java.math.BigInteger;
+
+/* ------------------------------------------------------------------ */
+/* BigDecimal -- Decimal arithmetic for Java                          */
+/* ------------------------------------------------------------------ */
+/* Copyright IBM Corporation, 1996, 2000.  All Rights Reserved.       */
+/*                                                                    */
+/* The BigDecimal class provides immutable arbitrary-precision        */
+/* floating point (including integer) decimal numbers.                */
+/*                                                                    */
+/* As the numbers are decimal, there is an exact correspondence       */
+/* between an instance of a BigDecimal object and its String          */
+/* representation; the BigDecimal class provides direct conversions   */
+/* to and from String and character array objects, and well as        */
+/* conversions to and from the Java primitive types (which may not    */
+/* be exact).                                                         */
+/* ------------------------------------------------------------------ */
+/* Notes:                                                             */
+/*                                                                    */
+/* 1. A BigDecimal object is never changed in value once constructed; */
+/*    this avoids the need for locking.  Note in particular that the  */
+/*    mantissa array may be shared between many BigDecimal objects,   */
+/*    so that once exposed it must not be altered.                    */
+/*                                                                    */
+/* 2. This class looks at MathContext class fields directly (for      */
+/*    performance).  It must not and does not change them.            */
+/*                                                                    */
+/* 3. Exponent checking is delayed until finish(), as we know         */
+/*    intermediate calculations cannot cause 31-bit overflow.         */
+/*    [This assertion depends on MAX_DIGITS in MathContext.]          */
+/*                                                                    */
+/* 4. Comments for the public API now follow the javadoc conventions. */
+/*    The NetRexx -comments option is used to pass these comments     */
+/*    through to the generated Java code (with -format, if desired).  */
+/*                                                                    */
+/* 5. System.arraycopy is faster than explicit loop as follows        */
+/*      Mean length 4:  equal                                         */
+/*      Mean length 8:  x2                                            */
+/*      Mean length 16: x3                                            */
+/*      Mean length 24: x4                                            */
+/*    From prior experience, we expect mean length a little below 8,  */
+/*    but arraycopy is still the one to use, in general, until later  */
+/*    measurements suggest otherwise.                                 */
+/*                                                                    */
+/* 6. 'DMSRCN' referred to below is the original (1981) IBM S/370     */
+/*    assembler code implementation of the algorithms below; it is    */
+/*    now called IXXRCN and is available with the OS/390 and VM/ESA   */
+/*    operating systems.                                              */
+/* ------------------------------------------------------------------ */
+/* Change History:                                                    */
+/* 1997.09.02 Initial version (derived from netrexx.lang classes)     */
+/* 1997.09.12 Add lostDigits checking                                 */
+/* 1997.10.06 Change mantissa to a byte array                         */
+/* 1997.11.22 Rework power [did not prepare arguments, etc.]          */
+/* 1997.12.13 multiply did not prepare arguments                      */
+/* 1997.12.14 add did not prepare and align arguments correctly       */
+/* 1998.05.02 0.07 packaging changes suggested by Sun and Oracle      */
+/* 1998.05.21 adjust remainder operator finalization                  */
+/* 1998.06.04 rework to pass MathContext to finish() and round()      */
+/* 1998.06.06 change format to use round(); support rounding modes    */
+/* 1998.06.25 rename to BigDecimal and begin merge                    */
+/*            zero can now have trailing zeros (i.e., exp\=0)         */
+/* 1998.06.28 new methods: movePointXxxx, scale, toBigInteger         */
+/*                         unscaledValue, valueof                     */
+/* 1998.07.01 improve byteaddsub to allow array reuse, etc.           */
+/* 1998.07.01 make null testing explicit to avoid JIT bug [Win32]     */
+/* 1998.07.07 scaled division  [divide(BigDecimal, int, int)]         */
+/* 1998.07.08 setScale, faster equals                                 */
+/* 1998.07.11 allow 1E6 (no sign) <sigh>; new double/float conversion */
+/* 1998.10.12 change package to com.ibm.math                          */
+/* 1998.12.14 power operator no longer rounds RHS [to match ANSI]     */
+/*            add toBigDecimal() and BigDecimal(java.math.BigDecimal) */
+/* 1998.12.29 improve byteaddsub by using table lookup                */
+/* 1999.02.04 lostdigits=0 behaviour rounds instead of digits+1 guard */
+/* 1999.02.05 cleaner code for BigDecimal(char[])                     */
+/* 1999.02.06 add javadoc comments                                    */
+/* 1999.02.11 format() changed from 7 to 2 method form                */
+/* 1999.03.05 null pointer checking is no longer explicit             */
+/* 1999.03.05 simplify; changes from discussion with J. Bloch:        */
+/*            null no longer permitted for MathContext; drop boolean, */
+/*            byte, char, float, short constructor, deprecate double  */
+/*            constructor, no blanks in string constructor, add       */
+/*            offset and length version of char[] constructor;        */
+/*            add valueOf(double); drop booleanValue, charValue;      */
+/*            add ...Exact versions of remaining convertors           */
+/* 1999.03.13 add toBigIntegerExact                                   */
+/* 1999.03.13 1.00 release to IBM Centre for Java Technology          */
+/* 1999.05.27 1.01 correct 0-0.2 bug under scaled arithmetic          */
+/* 1999.06.29 1.02 constructors should not allow exponent > 9 digits  */
+/* 1999.07.03 1.03 lost digits should not be checked if digits=0      */
+/* 1999.07.06      lost digits Exception message changed              */
+/* 1999.07.10 1.04 more work on 0-0.2 (scaled arithmetic)             */
+/* 1999.07.17      improve messages from pow method                   */
+/* 1999.08.08      performance tweaks                                 */
+/* 1999.08.15      fastpath in multiply                               */
+/* 1999.11.05 1.05 fix problem in intValueExact [e.g., 5555555555]    */
+/* 1999.12.22 1.06 remove multiply fastpath, and improve performance  */
+/* 2000.01.01      copyright update [Y2K has arrived]                 */
+/* 2000.06.18 1.08 no longer deprecate BigDecimal(double)             */
+/* ------------------------------------------------------------------ */
+
+
+
+
+
+/**
+ * The <code>BigDecimal</code> class implements immutable
+ * arbitrary-precision decimal numbers.  The methods of the
+ * <code>BigDecimal</code> class provide operations for fixed and
+ * floating point arithmetic, comparison, format conversions, and
+ * hashing.
+ * <p>
+ * As the numbers are decimal, there is an exact correspondence between
+ * an instance of a <code>BigDecimal</code> object and its
+ * <code>String</code> representation; the <code>BigDecimal</code> class
+ * provides direct conversions to and from <code>String</code> and
+ * character array (<code>char[]</code>) objects, as well as conversions
+ * to and from the Java primitive types (which may not be exact) and
+ * <code>BigInteger</code>.
+ * <p>
+ * In the descriptions of constructors and methods in this documentation,
+ * the value of a <code>BigDecimal</code> number object is shown as the
+ * result of invoking the <code>toString()</code> method on the object.
+ * The internal representation of a decimal number is neither defined
+ * nor exposed, and is not permitted to affect the result of any
+ * operation.
+ * <p>
+ * The floating point arithmetic provided by this class is defined by
+ * the ANSI X3.274-1996 standard, and is also documented at
+ * <code>http://www2.hursley.ibm.com/decimal</code>
+ * <br><i>[This URL will change.]</i>
+ *
+ * <h3>Operator methods</h3>
+ * <p>
+ * Operations on <code>BigDecimal</code> numbers are controlled by a
+ * {@link MathContext} object, which provides the context (precision and
+ * other information) for the operation. Methods that can take a
+ * <code>MathContext</code> parameter implement the standard arithmetic
+ * operators for <code>BigDecimal</code> objects and are known as
+ * <i>operator methods</i>.  The default settings provided by the
+ * constant {@link MathContext#DEFAULT} (<code>digits=9,
+ * form=SCIENTIFIC, lostDigits=false, roundingMode=ROUND_HALF_UP</code>)
+ * perform general-purpose floating point arithmetic to nine digits of
+ * precision.  The <code>MathContext</code> parameter must not be
+ * <code>null</code>.
+ * <p>
+ * Each operator method also has a version provided which does
+ * not take a <code>MathContext</code> parameter.  For this version of
+ * each method, the context settings used are <code>digits=0,
+ * form=PLAIN, lostDigits=false, roundingMode=ROUND_HALF_UP</code>;
+ * these settings perform fixed point arithmetic with unlimited
+ * precision, as defined for the original BigDecimal class in Java 1.1
+ * and Java 1.2.
+ * <p>
+ * For monadic operators, only the optional <code>MathContext</code>
+ * parameter is present; the operation acts upon the current object.
+ * <p>
+ * For dyadic operators, a <code>BigDecimal</code> parameter is always
+ * present; it must not be <code>null</code>.
+ * The operation acts with the current object being the left-hand operand
+ * and the <code>BigDecimal</code> parameter being the right-hand operand.
+ * <p>
+ * For example, adding two <code>BigDecimal</code> objects referred to
+ * by the names <code>award</code> and <code>extra</code> could be
+ * written as any of:
+ * <p><code>
+ *     award.add(extra)
+ * <br>award.add(extra, MathContext.DEFAULT)
+ * <br>award.add(extra, acontext)
+ * </code>
+ * <p>
+ * (where <code>acontext</code> is a <code>MathContext</code> object),
+ * which would return a <code>BigDecimal</code> object whose value is
+ * the result of adding <code>award</code> and <code>extra</code> under
+ * the appropriate context settings.
+ * <p>
+ * When a <code>BigDecimal</code> operator method is used, a set of
+ * rules define what the result will be (and, by implication, how the
+ * result would be represented as a character string).
+ * These rules are defined in the BigDecimal arithmetic documentation
+ * (see the URL above), but in summary:
+ * <ul>
+ * <li>Results are normally calculated with up to some maximum number of
+ * significant digits.
+ * For example, if the <code>MathContext</code> parameter for an operation
+ * were <code>MathContext.DEFAULT</code> then the result would be
+ * rounded to 9 digits; the division of 2 by 3 would then result in
+ * 0.666666667.
+ * <br>
+ * You can change the default of 9 significant digits by providing the
+ * method with a suitable <code>MathContext</code> object. This lets you
+ * calculate using as many digits as you need -- thousands, if necessary.
+ * Fixed point (scaled) arithmetic is indicated by using a
+ * <code>digits</code> setting of 0 (or omitting the
+ * <code>MathContext</code> parameter).
+ * <br>
+ * Similarly, you can change the algorithm used for rounding from the
+ * default "classic" algorithm.
+ * <li>
+ * In standard arithmetic (that is, when the <code>form</code> setting
+ * is not <code>PLAIN</code>), a zero result is always expressed as the
+ * single digit <code>'0'</code> (that is, with no sign, decimal point,
+ * or exponent part).
+ * <li>
+ * Except for the division and power operators in standard arithmetic,
+ * trailing zeros are preserved (this is in contrast to binary floating
+ * point operations and most electronic calculators, which lose the
+ * information about trailing zeros in the fractional part of results).
+ * <br>
+ * So, for example:
+ * <p><code>
+ *     new BigDecimal("2.40").add(     new BigDecimal("2"))      =&gt; "4.40"
+ * <br>new BigDecimal("2.40").subtract(new BigDecimal("2"))      =&gt; "0.40"
+ * <br>new BigDecimal("2.40").multiply(new BigDecimal("2"))      =&gt; "4.80"
+ * <br>new BigDecimal("2.40").divide(  new BigDecimal("2"), def) =&gt; "1.2"
+ * </code>
+ * <p>where the value on the right of the <code>=&gt;</code> would be the
+ * result of the operation, expressed as a <code>String</code>, and
+ * <code>def</code> (in this and following examples) refers to
+ * <code>MathContext.DEFAULT</code>).
+ * This preservation of trailing zeros is desirable for most
+ * calculations (including financial calculations).
+ * If necessary, trailing zeros may be easily removed using division by 1.
+ * <li>
+ * In standard arithmetic, exponential form is used for a result
+ * depending on its value and the current setting of <code>digits</code>
+ * (the default is 9 digits).
+ * If the number of places needed before the decimal point exceeds the
+ * <code>digits</code> setting, or the absolute value of the number is
+ * less than <code>0.000001</code>, then the number will be expressed in
+ * exponential notation; thus
+ * <p><code>
+ *   new BigDecimal("1e+6").multiply(new BigDecimal("1e+6"), def)
+ * </code>
+ * <p>results in <code>1E+12</code> instead of
+ * <code>1000000000000</code>, and
+ * <p><code>
+ *   new BigDecimal("1").divide(new BigDecimal("3E+10"), def)
+ * </code>
+ * <p>results in <code>3.33333333E-11</code> instead of
+ * <code>0.0000000000333333333</code>.
+ * <p>
+ * The form of the exponential notation (scientific or engineering) is
+ * determined by the <code>form</code> setting.
+ * <eul>
+ * <p>
+ * The names of methods in this class follow the conventions established
+ * by <code>java.lang.Number</code>, <code>java.math.BigInteger</code>,
+ * and <code>java.math.BigDecimal</code> in Java 1.1 and Java 1.2.
+ *
+ * @see     MathContext
+ * @version 1.08 2000.06.18
+ * @author  Mike Cowlishaw
+ */
+
+public class BigDecimal extends java.lang.Number implements java.io.Serializable,java.lang.Comparable{
+ private static final java.lang.String $0="BigDecimal.nrx";
+ 
+ 
+ 
+ /* ----- Constants ----- */
+ /* properties constant public */ // useful to others
+ /**
+  * The <code>BigDecimal</code> constant "0".
+  *
+  * @see #ONE
+  * @see #TEN
+  * @since IBM JDK 1.1.8
+  */
+ public static final com.ibm.math.BigDecimal ZERO=new com.ibm.math.BigDecimal((long)0); // use long as we want the int constructor
+ // .. to be able to use this, for speed
+ 
+ /**
+  * The <code>BigDecimal</code> constant "1".
+  *
+  * @see #TEN
+  * @see #ZERO
+  * @since IBM JDK 1.1.8
+  */
+ public static final com.ibm.math.BigDecimal ONE=new com.ibm.math.BigDecimal((long)1); // use long as we want the int constructor
+ // .. to be able to use this, for speed
+ 
+ /**
+  * The <code>BigDecimal</code> constant "10".
+  *
+  * @see #ONE
+  * @see #ZERO
+  * @since IBM JDK 1.1.8
+  */
+ public static final com.ibm.math.BigDecimal TEN=new com.ibm.math.BigDecimal(10);
+ 
+ // the rounding modes (copied here for upwards compatibility)
+ /**
+  * Rounding mode to round to a more positive number.
+  * See {@Link MathContext#ROUND_CEILING}.
+  */
+ public static final int ROUND_CEILING=com.ibm.math.MathContext.ROUND_CEILING;
+ 
+ /**
+  * Rounding mode to round towards zero.
+  * See {@Link MathContext#ROUND_DOWN}.
+  */
+ public static final int ROUND_DOWN=com.ibm.math.MathContext.ROUND_DOWN;
+ 
+ /**
+  * Rounding mode to round to a more negative number.
+  * See {@Link MathContext#ROUND_FLOOR}.
+  */
+ public static final int ROUND_FLOOR=com.ibm.math.MathContext.ROUND_FLOOR;
+ 
+ /**
+  * Rounding mode to round to nearest neighbor, where an equidistant
+  * value is rounded down.
+  * See {@Link MathContext#ROUND_HALF_DOWN}.
+  */
+ public static final int ROUND_HALF_DOWN=com.ibm.math.MathContext.ROUND_HALF_DOWN;
+ 
+ /**
+  * Rounding mode to round to nearest neighbor, where an equidistant
+  * value is rounded to the nearest even neighbor.
+  * See {@Link MathContext#ROUND_HALF_EVEN}.
+  */
+ public static final int ROUND_HALF_EVEN=com.ibm.math.MathContext.ROUND_HALF_EVEN;
+ 
+ /**
+  * Rounding mode to round to nearest neighbor, where an equidistant
+  * value is rounded up.
+  * See {@Link MathContext#ROUND_HALF_UP}.
+  */
+ public static final int ROUND_HALF_UP=com.ibm.math.MathContext.ROUND_HALF_UP;
+ 
+ /**
+  * Rounding mode to assert that no rounding is necessary.
+  * See {@Link MathContext#ROUND_UNNECESSARY}.
+  */
+ public static final int ROUND_UNNECESSARY=com.ibm.math.MathContext.ROUND_UNNECESSARY;
+ 
+ /**
+  * Rounding mode to round away from zero.
+  * See {@Link MathContext#ROUND_UP}.
+  */
+ public static final int ROUND_UP=com.ibm.math.MathContext.ROUND_UP;
+ 
+ /* properties constant private */ // locals
+ private static final byte ispos=1; // ind: indicates positive (must be 1)
+ private static final byte iszero=0; // ind: indicates zero     (must be 0)
+ private static final byte isneg=-1; // ind: indicates negative (must be -1)
+ // [later could add NaN, +/- infinity, here]
+ 
+ private static final int MinExp=-999999999; // minimum exponent allowed
+ private static final int MaxExp=999999999; // maximum exponent allowed
+ private static final int MinArg=-999999999; // minimum argument integer
+ private static final int MaxArg=999999999; // maximum argument integer
+ 
+ private static final com.ibm.math.MathContext plainMC=new com.ibm.math.MathContext(0,com.ibm.math.MathContext.PLAIN); // context for plain unlimited math
+ 
+ /* properties constant private unused */ // present but not referenced
+ 
+ // Serialization version
+ private static final long serialVersionUID=8245355804974198832L;
+ 
+ private static final java.lang.String copyright=" Copyright (c) IBM Corporation 1996, 2000.  All rights reserved. ";
+ 
+ /* properties static private */
+ // Precalculated constant arrays (used by byteaddsub)
+ private static byte bytecar[]=new byte[(90+99)+1]; // carry/borrow array
+ private static byte bytedig[]=diginit(); // next digit array
+ 
+ /* ----- Instance properties [all private and immutable] ----- */
+ /* properties private */
+ 
+ /**
+  * The indicator. This may take the values:
+  * <ul>
+  * <li>ispos  -- the number is positive
+  * <li>iszero -- the number is zero
+  * <li>isneg  -- the number is negative
+  * </ul>
+  *
+  * @serial
+  */
+ private byte ind; // assumed undefined
+ // Note: some code below assumes IND = Sign [-1, 0, 1], at present.
+ // We only need two bits for this, but use a byte [also permits
+ // smooth future extension].
+ 
+ /**
+  * The formatting style. This may take the values:
+  * <ul>
+  * <li>MathContext.PLAIN        -- no exponent needed
+  * <li>MathContext.SCIENTIFIC   -- scientific notation required
+  * <li>MathContext.ENGINEERING  -- engineering notation required
+  * </ul>
+  * <p>
+  * This property is an optimization; it allows us to defer number
+  * layout until it is actually needed as a string, hence avoiding
+  * unnecessary formatting.
+  *
+  * @serial
+  */
+ private byte form=(byte)com.ibm.math.MathContext.PLAIN; // assumed PLAIN
+ // We only need two bits for this, at present, but use a byte
+ // [again, to allow for smooth future extension]
+ 
+ /**
+  * The value of the mantissa.
+  * <p>
+  * Once constructed, this may become shared between several BigDecimal
+  * objects, so must not be altered.
+  * <p>
+  * For efficiency (speed), this is a byte array, with each byte
+  * taking a value of 0 -> 9.
+  * <p>
+  * If the first byte is 0 then the value of the number is zero (and
+  * mant.length=1, except when constructed from a plain number, for
+  * example, 0.000).
+  *
+  * @serial
+  */
+ private byte mant[]; // assumed null
+ 
+ /**
+  * The exponent.
+  * <p>
+  * For fixed point arithmetic, scale is <code>-exp</code>, and can
+  * apply to zero.
+  *
+  * Note that this property can have a value less than MinExp when
+  * the mantissa has more than one digit.
+  *
+  * @serial
+  */
+ private int exp;
+ // assumed 0
+ 
+ /* ---------------------------------------------------------------- */
+ /* Constructors                                                     */
+ /* ---------------------------------------------------------------- */
+ 
+ /**
+  * Constructs a <code>BigDecimal</code> object from a
+  * <code>java.math.BigDecimal</code>.
+  * <p>
+  * Constructs a <code>BigDecimal</code> as though the parameter had
+  * been represented as a <code>String</code> (using its
+  * <code>toString</code> method) and the
+  * {@link #BigDecimal(java.lang.String)} constructor had then been
+  * used.
+  * The parameter must not be <code>null</code>.
+  * <p>
+  * <i>(Note: this constructor is provided only in the
+  * <code>com.ibm.math</code> version of the BigDecimal class.
+  * It would not be present in a <code>java.math</code> version.)</i>
+  *
+  * @param bd The <code>BigDecimal</code> to be translated.
+  * @since IBM JDK 1.1.8
+  */
+ 
+ public BigDecimal(java.math.BigDecimal bd){
+  this(bd.toString());
+  return;}
+
+ /**
+  * Constructs a <code>BigDecimal</code> object from a
+  * <code>BigInteger</code>, with scale 0.
+  * <p>
+  * Constructs a <code>BigDecimal</code> which is the exact decimal
+  * representation of the <code>BigInteger</code>, with a scale of
+  * zero.
+  * The value of the <code>BigDecimal</code> is identical to the value
+  * of the <code>BigInteger</code>.
+  * The parameter must not be <code>null</code>.
+  * <p>
+  * The <code>BigDecimal</code> will contain only decimal digits,
+  * prefixed with a leading minus sign (hyphen) if the
+  * <code>BigInteger</code> is negative.  A leading zero will be
+  * present only if the <code>BigInteger</code> is zero.
+  *
+  * @param bi The <code>BigInteger</code> to be converted.
+  */
+ 
+ public BigDecimal(java.math.BigInteger bi){
+  this(bi.toString(10));
+  return;}
+ // exp remains 0
+ 
+ /**
+  * Constructs a <code>BigDecimal</code> object from a
+  * <code>BigInteger</code> and a scale.
+  * <p>
+  * Constructs a <code>BigDecimal</code> which is the exact decimal
+  * representation of the <code>BigInteger</code>, scaled by the
+  * second parameter, which may not be negative.
+  * The value of the <code>BigDecimal</code> is the
+  * <code>BigInteger</code> divided by ten to the power of the scale.
+  * The <code>BigInteger</code> parameter must not be
+  * <code>null</code>.
+  * <p>
+  * The <code>BigDecimal</code> will contain only decimal digits, (with
+  * an embedded decimal point followed by <code>scale</code> decimal
+  * digits if the scale is positive), prefixed with a leading minus
+  * sign (hyphen) if the <code>BigInteger</code> is negative.  A
+  * leading zero will be present only if the <code>BigInteger</code> is
+  * zero.
+  *
+  * @param  bi    The <code>BigInteger</code> to be converted.
+  * @param  scale The <code>int</code> specifying the scale.
+  * @throws NumberFormatException if the scale is negative.
+  */
+ 
+ public BigDecimal(java.math.BigInteger bi,int scale){
+  this(bi.toString(10));
+  if (scale<0) 
+   throw new java.lang.NumberFormatException("Negative scale:"+" "+scale);
+  exp=(int)-scale; // exponent is -scale
+  return;}
+
+ /**
+  * Constructs a <code>BigDecimal</code> object from an array of characters.
+  * <p>
+  * Constructs a <code>BigDecimal</code> as though a
+  * <code>String</code> had been constructed from the character array
+  * and the {@link #BigDecimal(java.lang.String)} constructor had then
+  * been used. The parameter must not be <code>null</code>.
+  * <p>
+  * Using this constructor is faster than using the
+  * <code>BigDecimal(String)</code> constructor if the string is
+  * already available in character array form.
+  *
+  * @param inchars The <code>char[]</code> array containing the number
+  *                to be converted.
+  * @throws NumberFormatException if the parameter is not a valid
+  *                number.
+  * @since IBM JDK 1.1.8
+  */
+ 
+ public BigDecimal(char inchars[]){
+  this(inchars,0,inchars.length);
+  return;}
+
+ /**
+  * Constructs a <code>BigDecimal</code> object from an array of characters.
+  * <p>
+  * Constructs a <code>BigDecimal</code> as though a
+  * <code>String</code> had been constructed from the character array
+  * (or a subarray of that array) and the
+  * {@link #BigDecimal(java.lang.String)} constructor had then been
+  * used. The first parameter must not be <code>null</code>, and the
+  * subarray must be wholly contained within it.
+  * <p>
+  * Using this constructor is faster than using the
+  * <code>BigDecimal(String)</code> constructor if the string is
+  * already available within a character array.
+  *
+  * @param inchars The <code>char[]</code> array containing the number
+  *                to be converted.
+  * @param offset  The <code>int</code> offset into the array of the
+  *                start of the number to be converted.
+  * @param length  The <code>int</code> length of the number.
+  * @throws NumberFormatException if the parameter is not a valid
+  *                number for any reason.
+  * @since IBM JDK 1.1.8
+  */
+ 
+ public BigDecimal(char inchars[],int offset,int length){super();
+  boolean exotic;
+  boolean hadexp;
+  int d;
+  int dotoff;
+  int last;
+  int i=0;
+  char si=0;
+  boolean eneg=false;
+  int k=0;
+  int elen=0;
+  int j=0;
+  char sj=0;
+  int dvalue=0;
+  int mag=0;
+  // This is the primary constructor; all incoming strings end up
+  // here; it uses explicit (inline) parsing for speed and to avoid
+  // generating intermediate (temporary) objects of any kind.
+  // 1998.06.25: exponent form built only if E/e in string
+  // 1998.06.25: trailing zeros not removed for zero
+  // 1999.03.06: no embedded blanks; allow offset and length
+  if (length<=0) 
+   bad(inchars); // bad conversion (empty string)
+  // [bad offset will raise array bounds exception]
+  
+  /* Handle and step past sign */
+  ind=ispos; // assume positive
+  if (inchars[0]==('-')) 
+   {
+    length--;
+    if (length==0) 
+     bad(inchars); // nothing after sign
+    ind=isneg;
+    offset++;
+   }
+  else 
+   if (inchars[0]==('+')) 
+    {
+     length--;
+     if (length==0) 
+      bad(inchars); // nothing after sign
+     offset++;
+    }
+  
+  /* We're at the start of the number */
+  exotic=false; // have extra digits
+  hadexp=false; // had explicit exponent
+  d=0; // count of digits found
+  dotoff=-1; // offset where dot was found
+  last=-1; // last character of mantissa
+  {int $1=length;i=offset;i:for(;$1>0;$1--,i++){
+   si=inchars[i];
+   if (si>='0')  // test for Arabic digit
+    if (si<='9') 
+     {
+      last=i;
+      d++; // still in mantissa
+      continue i;
+     }
+   if (si=='.') 
+    { // record and ignore
+     if (dotoff>=0) 
+      bad(inchars); // two dots
+     dotoff=i-offset; // offset into mantissa
+     continue i;
+    }
+   if (si!='e') 
+    if (si!='E') 
+     { // expect an extra digit
+      if ((!(java.lang.Character.isDigit(si)))) 
+       bad(inchars); // not a number
+      // defer the base 10 check until later to avoid extra method call
+      exotic=true; // will need conversion later
+      last=i;
+      d++; // still in mantissa
+      continue i;
+     }
+   /* Found 'e' or 'E' -- now process explicit exponent */
+   // 1998.07.11: sign no longer required
+   if ((i-offset)>(length-2)) 
+    bad(inchars); // no room for even one digit
+   eneg=false;
+   if ((inchars[i+1])==('-')) 
+    {
+     eneg=true;
+     k=i+2;
+    }
+   else 
+    if ((inchars[i+1])==('+')) 
+     k=i+2;
+    else 
+     k=i+1;
+   // k is offset of first expected digit
+   elen=length-((k-offset)); // possible number of digits
+   if ((elen==0)|(elen>9)) 
+    bad(inchars); // 0 or more than 9 digits
+   {int $2=elen;j=k;j:for(;$2>0;$2--,j++){
+    sj=inchars[j];
+    if (sj<'0') 
+     bad(inchars); // always bad
+    if (sj>'9') 
+     { // maybe an exotic digit
+      if ((!(java.lang.Character.isDigit(sj)))) 
+       bad(inchars); // not a number
+      dvalue=java.lang.Character.digit(sj,10); // check base
+      if (dvalue<0) 
+       bad(inchars); // not base 10
+     }
+    else 
+     dvalue=((int)(sj))-((int)('0'));
+    exp=(exp*10)+dvalue;
+    }
+   }/*j*/
+   if (eneg) 
+    exp=(int)-exp; // was negative
+   hadexp=true; // remember we had one
+   break i; // we are done
+   }
+  }/*i*/
+  
+  /* Here when all inspected */
+  if (d==0) 
+   bad(inchars); // no mantissa digits
+  if (dotoff>=0) 
+   exp=(exp+dotoff)-d; // adjust exponent if had dot
+  
+  /* strip leading zeros/dot (leave final if all 0's) */
+  {int $3=last-1;i=offset;i:for(;i<=$3;i++){
+   si=inchars[i];
+   if (si=='0') 
+    {
+     offset++;
+     dotoff--;
+     d--;
+    }
+   else 
+    if (si=='.') 
+     {
+      offset++; // step past dot
+      dotoff--;
+     }
+    else 
+     if (si<='9') 
+      break i;/* non-0 */
+     else 
+      {/* exotic */
+       if ((java.lang.Character.digit(si,10))!=0) 
+        break i; // non-0 or bad
+       // is 0 .. strip like '0'
+       offset++;
+       dotoff--;
+       d--;
+      }
+   }
+  }/*i*/
+  
+  /* Create the mantissa array */
+  mant=new byte[d]; // we know the length
+  j=offset; // input offset
+  if (exotic) 
+   {exotica:do{ // slow: check for exotica
+    {int $4=d;i=0;i:for(;$4>0;$4--,i++){
+     if (i==dotoff) 
+      j++; // at dot
+     sj=inchars[j];
+     if (sj<='9') 
+      mant[i]=(byte)(((int)(sj))-((int)('0')));/* easy */
+     else 
+      {
+       dvalue=java.lang.Character.digit(sj,10);
+       if (dvalue<0) 
+        bad(inchars); // not a number after all
+       mant[i]=(byte)dvalue;
+      }
+     j++;
+     }
+    }/*i*/
+   }while(false);}/*exotica*/
+  else 
+   {simple:do{
+    {int $5=d;i=0;i:for(;$5>0;$5--,i++){
+     if (i==dotoff) 
+      j++;
+     mant[i]=(byte)(((int)(inchars[j]))-((int)('0')));
+     j++;
+     }
+    }/*i*/
+   }while(false);}/*simple*/
+  
+  /* Looks good.  Set the sign indicator and form, as needed. */
+  // Trailing zeros are preserved
+  // The rule here for form is:
+  //   If no E-notation, then request plain notation
+  //   Otherwise act as though add(0,DEFAULT) and request scientific notation
+  // [form is already PLAIN]
+  if (mant[0]==0) 
+   {
+    ind=iszero; // force to show zero
+    // negative exponent is significant (e.g., -3 for 0.000) if plain
+    if (exp>0) 
+     exp=0; // positive exponent can be ignored
+    if (hadexp) 
+     { // zero becomes single digit from add
+      mant=ZERO.mant;
+      exp=0;
+     }
+   }
+  else 
+   { // non-zero
+    // [ind was set earlier]
+    // now determine form
+    if (hadexp) 
+     {
+      form=(byte)com.ibm.math.MathContext.SCIENTIFIC;
+      // 1999.06.29 check for overflow
+      mag=(exp+mant.length)-1; // true exponent in scientific notation
+      if ((mag<MinExp)|(mag>MaxExp)) 
+       bad(inchars);
+     }
+   }
+  // say 'BD(c[]): mant[0] mantlen exp ind form:' mant[0] mant.length exp ind form
+  return;
+  }
+
+ /**
+  * Constructs a <code>BigDecimal</code> object directly from a
+  * <code>double</code>.
+  * <p>
+  * Constructs a <code>BigDecimal</code> which is the exact decimal
+  * representation of the 64-bit signed binary floating point
+  * parameter.
+  * <p>
+  * Note that this constructor it an exact conversion; it does not give
+  * the same result as converting <code>num</code> to a
+  * <code>String</code> using the <code>Double.toString()</code> method
+  * and then using the {@link #BigDecimal(java.lang.String)}
+  * constructor.
+  * To get that result, use the static {@link #valueOf(double)}
+  * method to construct a <code>BigDecimal</code> from a
+  * <code>double</code>.
+  *
+  * @param num The <code>double</code> to be converted.
+  * @throws NumberFormatException if the parameter is infinite or
+  *            not a number.
+  */
+ 
+ public BigDecimal(double num){
+  // 1999.03.06: use exactly the old algorithm
+  // 2000.01.01: note that this constructor does give an exact result,
+  //             so perhaps it should not be deprecated
+  // 2000.06.18: no longer deprecated
+  this((new java.math.BigDecimal(num)).toString());
+  return;}
+
+ /**
+  * Constructs a <code>BigDecimal</code> object directly from a
+  * <code>int</code>.
+  * <p>
+  * Constructs a <code>BigDecimal</code> which is the exact decimal
+  * representation of the 32-bit signed binary integer parameter.
+  * The <code>BigDecimal</code> will contain only decimal digits,
+  * prefixed with a leading minus sign (hyphen) if the parameter is
+  * negative.
+  * A leading zero will be present only if the parameter is zero.
+  *
+  * @param num The <code>int</code> to be converted.
+  * @since IBM JDK 1.1.8
+  */
+ 
+ public BigDecimal(int num){super();
+  int mun;
+  int i=0;
+  // We fastpath commoners
+  if (num<=9) 
+   if (num>=(-9)) 
+    {singledigit:do{
+     // very common single digit case
+     {/*select*/
+     if (num==0)
+      {
+       mant=ZERO.mant;
+       ind=iszero;
+      }
+     else if (num==1)
+      {
+       mant=ONE.mant;
+       ind=ispos;
+      }
+     else if (num==(-1))
+      {
+       mant=ONE.mant;
+       ind=isneg;
+      }
+     else{
+      {
+       mant=new byte[1];
+       if (num>0) 
+        {
+         mant[0]=(byte)num;
+         ind=ispos;
+        }
+       else 
+        { // num<-1
+         mant[0]=(byte)((int)-num);
+         ind=isneg;
+        }
+      }
+     }
+     }
+     return;
+    }while(false);}/*singledigit*/
+  
+  /* We work on negative numbers so we handle the most negative number */
+  if (num>0) 
+   {
+    ind=ispos;
+    num=(int)-num;
+   }
+  else 
+   ind=isneg;/* negative */ // [0 case already handled]
+  // [it is quicker, here, to pre-calculate the length with
+  // one loop, then allocate exactly the right length of byte array,
+  // then re-fill it with another loop]
+  mun=num; // working copy
+  {i=9;i:for(;;i--){
+   mun=mun/10;
+   if (mun==0) 
+    break i;
+   }
+  }/*i*/
+  // i is the position of the leftmost digit placed
+  mant=new byte[10-i];
+  {i=(10-i)-1;i:for(;;i--){
+   mant[i]=(byte)-(((byte)(num%10)));
+   num=num/10;
+   if (num==0) 
+    break i;
+   }
+  }/*i*/
+  return;
+  }
+
+ /**
+  * Constructs a <code>BigDecimal</code> object directly from a
+  * <code>long</code>.
+  * <p>
+  * Constructs a <code>BigDecimal</code> which is the exact decimal
+  * representation of the 64-bit signed binary integer parameter.
+  * The <code>BigDecimal</code> will contain only decimal digits,
+  * prefixed with a leading minus sign (hyphen) if the parameter is
+  * negative.
+  * A leading zero will be present only if the parameter is zero.
+  *
+  * @param num The <code>long</code> to be converted.
+  * @since IBM JDK 1.1.8
+  */
+ 
+ public BigDecimal(long num){super();
+  long mun;
+  int i=0;
+  // Not really worth fastpathing commoners in this constructor [also,
+  // we use this to construct the static constants].
+  // This is much faster than: this(String.valueOf(num).toCharArray())
+  /* We work on negative num so we handle the most negative number */
+  if (num>0) 
+   {
+    ind=ispos;
+    num=(long)-num;
+   }
+  else 
+   if (num==0) 
+    ind=iszero;
+   else 
+    ind=isneg;/* negative */
+  mun=num;
+  {i=18;i:for(;;i--){
+   mun=mun/10;
+   if (mun==0) 
+    break i;
+   }
+  }/*i*/
+  // i is the position of the leftmost digit placed
+  mant=new byte[19-i];
+  {i=(19-i)-1;i:for(;;i--){
+   mant[i]=(byte)-(((byte)(num%10)));
+   num=num/10;
+   if (num==0) 
+    break i;
+   }
+  }/*i*/
+  return;
+  }
+
+ /**
+  * Constructs a <code>BigDecimal</code> object from a <code>String</code>.
+  * <p>
+  * Constructs a <code>BigDecimal</code> from the parameter, which must
+  * not be <code>null</code> and must represent a valid <i>number</i>,
+  * as described formally in the documentation referred to
+  * {@link BigDecimal above}.
+  * <p>
+  * In summary, numbers in <code>String</code> form must have at least
+  * one digit, may have a leading sign, may have a decimal point, and
+  * exponential notation may be used.  They follow conventional syntax,
+  * and may not contain blanks.
+  * <p>
+  * Some valid strings from which a <code>BigDecimal</code> might
+  * be constructed are:
+  * <pre>
+  *       "0"         -- Zero
+  *      "12"         -- A whole number
+  *     "-76"         -- A signed whole number
+  *      "12.70"      -- Some decimal places
+  *     "+0.003"      -- Plus sign is allowed
+  *      "17."        -- The same as 17
+  *        ".5"       -- The same as 0.5
+  *      "4E+9"       -- Exponential notation
+  *       "0.73e-7"   -- Exponential notation
+  * </pre>
+  * <p>
+  * (Exponential notation means that the number includes an optional
+  * sign and a power of ten following an '</code>E</code>' that
+  * indicates how the decimal point will be shifted.  Thus the
+  * <code>"4E+9"</code> above is just a short way of writing
+  * <code>4000000000</code>, and the <code>"0.73e-7"</code> is short
+  * for <code>0.000000073</code>.)
+  * <p>
+  * The <code>BigDecimal</code> constructed from the String is in a
+  * standard form, with no blanks, as though the
+  * {@link #add(BigDecimal)} method had been used to add zero to the
+  * number with unlimited precision.
+  * If the string uses exponential notation (that is, includes an
+  * <code>e</code> or an <code>E</code>), then the
+  * <code>BigDecimal</code> number will be expressed in scientific
+  * notation (where the power of ten is adjusted so there is a single
+  * non-zero digit to the left of the decimal point); in this case if
+  * the number is zero then it will be expressed as the single digit 0,
+  * and if non-zero it will have an exponent unless that exponent would
+  * be 0.  The exponent must fit in nine digits both before and after it
+  * is expressed in scientific notation.
+  * <p>
+  * Any digits in the parameter must be decimal; that is,
+  * <code>Character.digit(c, 10)</code> (where </code>c</code> is the
+  * character in question) would not return -1.
+  *
+  * @param string The <code>String</code> to be converted.
+  * @throws NumberFormatException if the parameter is not a valid
+  * number.
+  */
+ 
+ public BigDecimal(java.lang.String string){
+  this(string.toCharArray(),0,string.length());
+  return;}
+
+ /* <sgml> Make a default BigDecimal object for local use. </sgml> */
+ 
+ private BigDecimal(){super();
+  return;
+  }
+
+ /* ---------------------------------------------------------------- */
+ /* Operator methods [methods which take a context parameter]        */
+ /* ---------------------------------------------------------------- */
+ 
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is the absolute
+  * value of this <code>BigDecimal</code>.
+  * <p>
+  * The same as {@link #abs(MathContext)}, where the context is
+  * <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will
+  * be <code>this.scale()</code>
+  *
+  * @return A <code>BigDecimal</code> whose value is the absolute
+  *         value of this <code>BigDecimal</code>.
+  */
+ 
+ public com.ibm.math.BigDecimal abs(){
+  return this.abs(plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is the absolute value
+  * of this <code>BigDecimal</code>.
+  * <p>
+  * If the current object is zero or positive, then the same result as
+  * invoking the {@link #plus(MathContext)} method with the same
+  * parameter is returned.
+  * Otherwise, the same result as invoking the
+  * {@link #negate(MathContext)} method with the same parameter is
+  * returned.
+  *
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is the absolute
+  *             value of this <code>BigDecimal</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal abs(com.ibm.math.MathContext set){
+  if (this.ind==isneg) 
+   return this.negate(set);
+  return this.plus(set);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this+rhs</code>, using fixed point arithmetic.
+  * <p>
+  * The same as {@link #add(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will be
+  * the maximum of the scales of the two operands.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the addition.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             <code>this+rhs</code>, using fixed point arithmetic.
+  */
+ 
+ public com.ibm.math.BigDecimal add(com.ibm.math.BigDecimal rhs){
+  return this.add(rhs,plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is <code>this+rhs</code>.
+  * <p>
+  * Implements the addition (<b><code>+</code></b>) operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the addition.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             <code>this+rhs</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal add(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  com.ibm.math.BigDecimal lhs;
+  int reqdig;
+  com.ibm.math.BigDecimal res;
+  byte usel[];
+  int usellen;
+  byte user[];
+  int userlen;
+  int newlen=0;
+  int tlen=0;
+  int mult=0;
+  byte t[]=null;
+  int ia=0;
+  int ib=0;
+  int ea=0;
+  int eb=0;
+  byte ca=0;
+  byte cb=0;
+  /* determine requested digits and form */
+  if (set.lostDigits) 
+   checkdigits(rhs,set.digits);
+  lhs=this; // name for clarity and proxy
+  
+  /* Quick exit for add floating 0 */
+  // plus() will optimize to return same object if possible
+  if (lhs.ind==0) 
+   if (set.form!=com.ibm.math.MathContext.PLAIN) 
+    return rhs.plus(set);
+  if (rhs.ind==0) 
+   if (set.form!=com.ibm.math.MathContext.PLAIN) 
+    return lhs.plus(set);
+  
+  /* Prepare numbers (round, unless unlimited precision) */
+  reqdig=set.digits; // local copy (heavily used)
+  if (reqdig>0) 
+   {
+    if (lhs.mant.length>reqdig) 
+     lhs=clone(lhs).round(set);
+    if (rhs.mant.length>reqdig) 
+     rhs=clone(rhs).round(set);
+   // [we could reuse the new LHS for result in this case]
+   }
+  
+  res=new com.ibm.math.BigDecimal(); // build result here
+  
+  /* Now see how much we have to pad or truncate lhs or rhs in order
+     to align the numbers.  If one number is much larger than the
+     other, then the smaller cannot affect the answer [but we may
+     still need to pad with up to DIGITS trailing zeros]. */
+  // Note sign may be 0 if digits (reqdig) is 0
+  // usel and user will be the byte arrays passed to the adder; we'll
+  // use them on all paths except quick exits
+  usel=lhs.mant;
+  usellen=lhs.mant.length;
+  user=rhs.mant;
+  userlen=rhs.mant.length;
+  {padder:do{/*select*/
+  if (lhs.exp==rhs.exp)
+   {/* no padding needed */
+    // This is the most common, and fastest, path
+    res.exp=lhs.exp;
+   }
+  else if (lhs.exp>rhs.exp)
+   { // need to pad lhs and/or truncate rhs
+    newlen=(usellen+lhs.exp)-rhs.exp;
+    /* If, after pad, lhs would be longer than rhs by digits+1 or
+       more (and digits>0) then rhs cannot affect answer, so we only
+       need to pad up to a length of DIGITS+1. */
+    if (newlen>=((userlen+reqdig)+1)) 
+     if (reqdig>0) 
+      {
+       // LHS is sufficient
+       res.mant=usel;
+       res.exp=lhs.exp;
+       res.ind=lhs.ind;
+       if (usellen<reqdig) 
+        { // need 0 padding
+         res.mant=extend(lhs.mant,reqdig);
+         res.exp=res.exp-((reqdig-usellen));
+        }
+       return res.finish(set,false);
+      }
+    // RHS may affect result
+    res.exp=rhs.exp; // expected final exponent
+    if (newlen>(reqdig+1)) 
+     if (reqdig>0) 
+      {
+       // LHS will be max; RHS truncated
+       tlen=(newlen-reqdig)-1; // truncation length
+       userlen=userlen-tlen;
+       res.exp=res.exp+tlen;
+       newlen=reqdig+1;
+      }
+    if (newlen>usellen) 
+     usellen=newlen; // need to pad LHS
+   }
+  else{ // need to pad rhs and/or truncate lhs
+   newlen=(userlen+rhs.exp)-lhs.exp;
+   if (newlen>=((usellen+reqdig)+1)) 
+    if (reqdig>0) 
+     {
+      // RHS is sufficient
+      res.mant=user;
+      res.exp=rhs.exp;
+      res.ind=rhs.ind;
+      if (userlen<reqdig) 
+       { // need 0 padding
+        res.mant=extend(rhs.mant,reqdig);
+        res.exp=res.exp-((reqdig-userlen));
+       }
+      return res.finish(set,false);
+     }
+   // LHS may affect result
+   res.exp=lhs.exp; // expected final exponent
+   if (newlen>(reqdig+1)) 
+    if (reqdig>0) 
+     {
+      // RHS will be max; LHS truncated
+      tlen=(newlen-reqdig)-1; // truncation length
+      usellen=usellen-tlen;
+      res.exp=res.exp+tlen;
+      newlen=reqdig+1;
+     }
+   if (newlen>userlen) 
+    userlen=newlen; // need to pad RHS
+  }
+  }while(false);}/*padder*/
+  
+  /* OK, we have aligned mantissas.  Now add or subtract. */
+  // 1998.06.27 Sign may now be 0 [e.g., 0.000] .. treat as positive
+  // 1999.05.27 Allow for 00 on lhs [is not larger than 2 on rhs]
+  // 1999.07.10 Allow for 00 on rhs [is not larger than 2 on rhs]
+  if (lhs.ind==iszero) 
+   res.ind=ispos;
+  else 
+   res.ind=lhs.ind; // likely sign, all paths
+  if (((lhs.ind==isneg)?1:0)==((rhs.ind==isneg)?1:0))  // same sign, 0 non-negative
+   mult=1;
+  else 
+   {signdiff:do{ // different signs, so subtraction is needed
+    mult=-1; // will cause subtract
+    /* Before we can subtract we must determine which is the larger,
+       as our add/subtract routine only handles non-negative results
+       so we may need to swap the operands. */
+    {swaptest:do{/*select*/
+    if (rhs.ind==iszero)
+     ; // original A bigger
+    else if ((usellen<userlen)|(lhs.ind==iszero))
+     { // original B bigger
+      t=usel;
+      usel=user;
+      user=t; // swap
+      tlen=usellen;
+      usellen=userlen;
+      userlen=tlen; // ..
+      res.ind=(byte)-res.ind; // and set sign
+     }
+    else if (usellen>userlen)
+     ; // original A bigger
+    else{
+     {/* logical lengths the same */ // need compare
+      /* may still need to swap: compare the strings */
+      ia=0;
+      ib=0;
+      ea=usel.length-1;
+      eb=user.length-1;
+      {compare:for(;;){
+       if (ia<=ea) 
+        ca=usel[ia];
+       else 
+        {
+         if (ib>eb) 
+          {/* identical */
+           if (set.form!=com.ibm.math.MathContext.PLAIN) 
+            return ZERO;
+           // [if PLAIN we must do the subtract, in case of 0.000 results]
+           break compare;
+          }
+         ca=(byte)0;
+        }
+       if (ib<=eb) 
+        cb=user[ib];
+       else 
+        cb=(byte)0;
+       if (ca!=cb) 
+        {
+         if (ca<cb) 
+          {/* swap needed */
+           t=usel;
+           usel=user;
+           user=t; // swap
+           tlen=usellen;
+           usellen=userlen;
+           userlen=tlen; // ..
+           res.ind=(byte)-res.ind;
+          }
+         break compare;
+        }
+       /* mantissas the same, so far */
+       ia++;
+       ib++;
+       }
+      }/*compare*/
+     } // lengths the same
+    }
+    }while(false);}/*swaptest*/
+   }while(false);}/*signdiff*/
+  
+  /* here, A is > B if subtracting */
+  // add [A+B*1] or subtract [A+(B*-1)]
+  res.mant=byteaddsub(usel,usellen,user,userlen,mult,false);
+  // [reuse possible only after chop; accounting makes not worthwhile]
+  
+  // Finish() rounds before stripping leading 0's, then sets form, etc.
+  return res.finish(set,false);
+  }
+
+ /**
+  * Compares this <code>BigDecimal</code> to another, using unlimited
+  * precision.
+  * <p>
+  * The same as {@link #compareTo(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @return     An <code>int</code> whose value is -1, 0, or 1 as
+  *             <code>this</code> is numerically less than, equal to,
+  *             or greater than <code>rhs</code>.
+  * @see    #compareTo(Object)
+  */
+ 
+ public int compareTo(com.ibm.math.BigDecimal rhs){
+  return this.compareTo(rhs,plainMC);
+  }
+
+ /**
+  * Compares this <code>BigDecimal</code> to another.
+  * <p>
+  * Implements numeric comparison,
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns a result of type <code>int</code>.
+  * <p>
+  * The result will be:
+  * <table cellpadding=2><tr>
+  * <td align=right><b>-1</b></td>
+  * <td>if the current object is less than the first parameter</td>
+  * </tr><tr>
+  * <td align=right><b>0</b></td>
+  * <td>if the current object is equal to the first parameter</td>
+  * </tr><tr>
+  * <td align=right><b>1</b></td>
+  * <td>if the current object is greater than the first parameter.</td>
+  * </tr></table>
+  * <p>
+  * A {@link #compareTo(Object)} method is also provided.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     An <code>int</code> whose value is -1, 0, or 1 as
+  *             <code>this</code> is numerically less than, equal to,
+  *             or greater than <code>rhs</code>.
+  * @see    #compareTo(Object)
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public int compareTo(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  int thislength=0;
+  int i=0;
+  com.ibm.math.BigDecimal newrhs;
+  // rhs=null will raise NullPointerException, as per Comparable interface
+  if (set.lostDigits) 
+   checkdigits(rhs,set.digits);
+  // [add will recheck in slowpath cases .. but would report -rhs]
+  if ((this.ind==rhs.ind)&(this.exp==rhs.exp)) 
+   {
+    /* sign & exponent the same [very common] */
+    thislength=this.mant.length;
+    if (thislength<rhs.mant.length) 
+     return (byte)-this.ind;
+    if (thislength>rhs.mant.length) 
+     return this.ind;
+    /* lengths are the same; we can do a straight mantissa compare
+       unless maybe rounding [rounding is very unusual] */
+    if ((thislength<=set.digits)|(set.digits==0)) 
+     {
+      {int $6=thislength;i=0;i:for(;$6>0;$6--,i++){
+       if (this.mant[i]<rhs.mant[i]) 
+        return (byte)-this.ind;
+       if (this.mant[i]>rhs.mant[i]) 
+        return this.ind;
+       }
+      }/*i*/
+      return 0; // identical
+     }
+   /* drop through for full comparison */
+   }
+  else 
+   {
+    /* More fastpaths possible */
+    if (this.ind<rhs.ind) 
+     return -1;
+    if (this.ind>rhs.ind) 
+     return 1;
+   }
+  /* carry out a subtract to make the comparison */
+  newrhs=clone(rhs); // safe copy
+  newrhs.ind=(byte)-newrhs.ind; // prepare to subtract
+  return this.add(newrhs,set).ind; // add, and return sign of result
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this/rhs</code>, using fixed point arithmetic.
+  * <p>
+  * The same as {@link #divide(BigDecimal, int)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the rounding mode is {@link MathContext#ROUND_HALF_UP}.
+  *
+  * The length of the decimal part (the scale) of the result will be
+  * the same as the scale of the current object, if the latter were
+  * formatted without exponential notation.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the division.
+  * @return     A plain <code>BigDecimal</code> whose value is
+  *             <code>this/rhs</code>, using fixed point arithmetic.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal divide(com.ibm.math.BigDecimal rhs){
+  return this.dodivide('D',rhs,plainMC,-1);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this/rhs</code>, using fixed point arithmetic and a
+  * rounding mode.
+  * <p>
+  * The same as {@link #divide(BigDecimal, int, int)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the second parameter is <code>this.scale()</code>, and
+  * the third is <code>round</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will
+  * therefore be the same as the scale of the current object, if the
+  * latter were formatted without exponential notation.
+  * <p>
+  * @param  rhs   The <code>BigDecimal</code> for the right hand side of
+  *               the division.
+  * @param  round The <code>int</code> rounding mode to be used for
+  *               the division (see the {@link MathContext} class).
+  * @return       A plain <code>BigDecimal</code> whose value is
+  *               <code>this/rhs</code>, using fixed point arithmetic
+  *               and the specified rounding mode.
+  * @throws IllegalArgumentException if <code>round</code> is not a
+  *               valid rounding mode.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @throws ArithmeticException if <code>round</code> is {@link
+  *               MathContext#ROUND_UNNECESSARY} and
+  *               <code>this.scale()</code> is insufficient to
+  *               represent the result exactly.
+  */
+ 
+ public com.ibm.math.BigDecimal divide(com.ibm.math.BigDecimal rhs,int round){
+  com.ibm.math.MathContext set;
+  set=new com.ibm.math.MathContext(0,com.ibm.math.MathContext.PLAIN,false,round); // [checks round, too]
+  return this.dodivide('D',rhs,set,-1); // take scale from LHS
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this/rhs</code>, using fixed point arithmetic and a
+  * given scale and rounding mode.
+  * <p>
+  * The same as {@link #divide(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * <code>new MathContext(0, MathContext.PLAIN, false, round)</code>,
+  * except that the length of the decimal part (the scale) to be used
+  * for the result is explicit rather than being taken from
+  * <code>this</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will be
+  * the same as the scale of the current object, if the latter were
+  * formatted without exponential notation.
+  * <p>
+  * @param  rhs   The <code>BigDecimal</code> for the right hand side of
+  *               the division.
+  * @param  scale The <code>int</code> scale to be used for the result.
+  * @param  round The <code>int</code> rounding mode to be used for
+  *               the division (see the {@link MathContext} class).
+  * @return       A plain <code>BigDecimal</code> whose value is
+  *               <code>this/rhs</code>, using fixed point arithmetic
+  *               and the specified rounding mode.
+  * @throws IllegalArgumentException if <code>round</code> is not a
+  *               valid rounding mode.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @throws ArithmeticException if <code>scale</code> is negative.
+  * @throws ArithmeticException if <code>round</code> is {@link
+  *               MathContext#ROUND_UNNECESSARY} and <code>scale</code>
+  *               is insufficient to represent the result exactly.
+  */
+ 
+ public com.ibm.math.BigDecimal divide(com.ibm.math.BigDecimal rhs,int scale,int round){
+  com.ibm.math.MathContext set;
+  if (scale<0) 
+   throw new java.lang.ArithmeticException("Negative scale:"+" "+scale);
+  set=new com.ibm.math.MathContext(0,com.ibm.math.MathContext.PLAIN,false,round); // [checks round]
+  return this.dodivide('D',rhs,set,scale);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is <code>this/rhs</code>.
+  * <p>
+  * Implements the division (<b><code>/</code></b>) operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the division.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             <code>this/rhs</code>.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal divide(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  return this.dodivide('D',rhs,set,-1);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is the integer
+  * part of <code>this/rhs</code>.
+  * <p>
+  * The same as {@link #divideInteger(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the integer division.
+  * @return     A <code>BigDecimal</code> whose value is the integer
+  *             part of <code>this/rhs</code>.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal divideInteger(com.ibm.math.BigDecimal rhs){
+  // scale 0 to drop .000 when plain
+  return this.dodivide('I',rhs,plainMC,0);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is the integer
+  * part of <code>this/rhs</code>.
+  * <p>
+  * Implements the integer division operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the integer division.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is the integer
+  *             part of <code>this/rhs</code>.
+  * @throws ArithmeticException if <code>rhs</code> is zero.
+  * @throws ArithmeticException if the result will not fit in the
+  *             number of digits specified for the context.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal divideInteger(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  // scale 0 to drop .000 when plain
+  return this.dodivide('I',rhs,set,0);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * the maximum of <code>this</code> and <code>rhs</code>.
+  * <p>
+  * The same as {@link #max(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             the maximum of <code>this</code> and <code>rhs</code>.
+  */
+ 
+ public com.ibm.math.BigDecimal max(com.ibm.math.BigDecimal rhs){
+  return this.max(rhs,plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is
+  * the maximum of <code>this</code> and <code>rhs</code>.
+  * <p>
+  * Returns the larger of the current object and the first parameter.
+  * <p>
+  * If calling the {@link #compareTo(BigDecimal, MathContext)} method
+  * with the same parameters would return <code>1</code> or
+  * <code>0</code>, then the result of calling the
+  * {@link #plus(MathContext)} method on the current object (using the
+  * same <code>MathContext</code> parameter) is returned.
+  * Otherwise, the result of calling the {@link #plus(MathContext)}
+  * method on the first parameter object (using the same
+  * <code>MathContext</code> parameter) is returned.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             the maximum of <code>this</code> and <code>rhs</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal max(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  if ((this.compareTo(rhs,set))>=0) 
+   return this.plus(set);
+  else 
+   return rhs.plus(set);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * the minimum of <code>this</code> and <code>rhs</code>.
+  * <p>
+  * The same as {@link #min(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             the minimum of <code>this</code> and <code>rhs</code>.
+  */
+ 
+ public com.ibm.math.BigDecimal min(com.ibm.math.BigDecimal rhs){
+  return this.min(rhs,plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is
+  * the minimum of <code>this</code> and <code>rhs</code>.
+  * <p>
+  * Returns the smaller of the current object and the first parameter.
+  * <p>
+  * If calling the {@link #compareTo(BigDecimal, MathContext)} method
+  * with the same parameters would return <code>-1</code> or
+  * <code>0</code>, then the result of calling the
+  * {@link #plus(MathContext)} method on the current object (using the
+  * same <code>MathContext</code> parameter) is returned.
+  * Otherwise, the result of calling the {@link #plus(MathContext)}
+  * method on the first parameter object (using the same
+  * <code>MathContext</code> parameter) is returned.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the comparison.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             the minimum of <code>this</code> and <code>rhs</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal min(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  if ((this.compareTo(rhs,set))<=0) 
+   return this.plus(set);
+  else 
+   return rhs.plus(set);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this*rhs</code>, using fixed point arithmetic.
+  * <p>
+  * The same as {@link #add(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will be
+  * the sum of the scales of the operands, if they were formatted
+  * without exponential notation.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the multiplication.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             <code>this*rhs</code>, using fixed point arithmetic.
+  */
+ 
+ public com.ibm.math.BigDecimal multiply(com.ibm.math.BigDecimal rhs){
+  return this.multiply(rhs,plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is <code>this*rhs</code>.
+  * <p>
+  * Implements the multiplication (<b><code>*</code></b>) operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  *
+  * @param  rhs The <code>BigDecimal</code> for the right hand side of
+  *             the multiplication.
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return     A <code>BigDecimal</code> whose value is
+  *             <code>this*rhs</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal multiply(com.ibm.math.BigDecimal rhs,com.ibm.math.MathContext set){
+  com.ibm.math.BigDecimal lhs;
+  int padding;
+  int reqdig;
+  byte multer[]=null;
+  byte multand[]=null;
+  int multandlen;
+  int acclen=0;
+  com.ibm.math.BigDecimal res;
+  byte acc[];
+  int n=0;
+  byte mult=0;
+  if (set.lostDigits) 
+   checkdigits(rhs,set.digits);
+  lhs=this; // name for clarity and proxy
+  
+  /* Prepare numbers (truncate, unless unlimited precision) */
+  padding=0; // trailing 0's to add
+  reqdig=set.digits; // local copy
+  if (reqdig>0) 
+   {
+    if (lhs.mant.length>reqdig) 
+     lhs=clone(lhs).round(set);
+    if (rhs.mant.length>reqdig) 
+     rhs=clone(rhs).round(set);
+   // [we could reuse the new LHS for result in this case]
+   }
+  else 
+   {/* unlimited */
+    // fixed point arithmetic will want every trailing 0; we add these
+    // after the calculation rather than before, for speed.
+    if (lhs.exp>0) 
+     padding=padding+lhs.exp;
+    if (rhs.exp>0) 
+     padding=padding+rhs.exp;
+   }
+  
+  // For best speed, as in DMSRCN, we use the shorter number as the
+  // multiplier and the longer as the multiplicand.
+  // 1999.12.22: We used to special case when the result would fit in
+  //             a long, but with Java 1.3 this gave no advantage.
+  if (lhs.mant.length<rhs.mant.length) 
+   {
+    multer=lhs.mant;
+    multand=rhs.mant;
+   }
+  else 
+   {
+    multer=rhs.mant;
+    multand=lhs.mant;
+   }
+  
+  /* Calculate how long result byte array will be */
+  multandlen=(multer.length+multand.length)-1; // effective length
+  // optimize for 75% of the cases where a carry is expected...
+  if ((multer[0]*multand[0])>9) 
+   acclen=multandlen+1;
+  else 
+   acclen=multandlen;
+  
+  /* Now the main long multiplication loop */
+  res=new com.ibm.math.BigDecimal(); // where we'll build result
+  acc=new byte[acclen]; // accumulator, all zeros
+  // 1998.07.01: calculate from left to right so that accumulator goes
+  // to likely final length on first addition; this avoids a one-digit
+  // extension (and object allocation) each time around the loop.
+  // Initial number therefore has virtual zeros added to right.
+  {int $7=multer.length;n=0;n:for(;$7>0;$7--,n++){
+   mult=multer[n];
+   if (mult!=0) 
+    { // [optimization]
+     // accumulate [accumulator is reusable array]
+     acc=byteaddsub(acc,acc.length,multand,multandlen,mult,true);
+    }
+   // divide multiplicand by 10 for next digit to right
+   multandlen--; // 'virtual length'
+   }
+  }/*n*/
+  
+  res.ind=(byte)(lhs.ind*rhs.ind); // final sign
+  res.exp=(lhs.exp+rhs.exp)-padding; // final exponent
+  // [overflow is checked by finish]
+  
+  /* add trailing zeros to the result, if necessary */
+  if (padding==0) 
+   res.mant=acc;
+  else 
+   res.mant=extend(acc,acc.length+padding); // add trailing 0s
+  return res.finish(set,false);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>-this</code>.
+  * <p>
+  * The same as {@link #negate(MathContext)}, where the context is
+  * <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will be
+  * be <code>this.scale()</code>
+  *
+  *
+  * @return A <code>BigDecimal</code> whose value is
+  *         <code>-this</code>.
+  */
+ 
+ public com.ibm.math.BigDecimal negate(){
+  return this.negate(plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is <code>-this</code>.
+  * <p>
+  * Implements the negation (Prefix <b><code>-</code></b>) operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  *
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return A <code>BigDecimal</code> whose value is
+  *         <code>-this</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal negate(com.ibm.math.MathContext set){
+  com.ibm.math.BigDecimal res;
+  // Originally called minus(), changed to matched Java precedents
+  // This simply clones, flips the sign, and possibly rounds
+  if (set.lostDigits) 
+   checkdigits((com.ibm.math.BigDecimal)null,set.digits);
+  res=clone(this); // safe copy
+  res.ind=(byte)-res.ind;
+  return res.finish(set,false);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>+this</code>.
+  * Note that <code>this</code> is not necessarily a
+  * plain <code>BigDecimal</code>, but the result will always be.
+  * <p>
+  * The same as {@link #plus(MathContext)}, where the context is
+  * <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The length of the decimal part (the scale) of the result will be
+  * be <code>this.scale()</code>
+  *
+  * @return A <code>BigDecimal</code> whose value is
+  *         <code>+this</code>.
+  */
+ 
+ public com.ibm.math.BigDecimal plus(){
+  return this.plus(plainMC);
+  }
+
+ /**
+  * Returns a <code>BigDecimal</code> whose value is
+  * <code>+this</code>.
+  * <p>
+  * Implements the plus (Prefix <b><code>+</code></b>) operator
+  * (as defined in the decimal documentation, see {@link BigDecimal
+  * class header}),
+  * and returns the result as a <code>BigDecimal</code> object.
+  * <p>
+  * This method is useful for rounding or otherwise applying a context
+  * to a decimal value.
+  *
+  * @param  set The <code>MathContext</code> arithmetic settings.
+  * @return A <code>BigDecimal</code> whose value is
+  *         <code>+this</code>.
+  * @since  IBM JDK 1.1.8
+  */
+ 
+ public com.ibm.math.BigDecimal plus(com.ibm.math.MathContext set){
+  // This clones and forces the result to the new settings
+  // May return same object
+  if (set.lostDigits) 
+   checkdigits((com.ibm.math.BigDecimal)null,set.digits);
+  // Optimization: returns same object for some common cases
+  if (set.form==com.ibm.math.MathContext.PLAIN) 
+   if (this.form==com.ibm.math.MathContext.PLAIN) 
+    {
+     if (this.mant.length<=set.digits) 
+      return this;
+     if (set.digits==0) 
+      return this;
+    }
+  return clone(this).finish(set,false);
+  }
+
+ /**
+  * Returns a plain <code>BigDecimal</code> whose value is
+  * <code>this**rhs</code>, using fixed point arithmetic.
+  * <p>
+  * The same as {@link #pow(BigDecimal, MathContext)},
+  * where the <code>BigDecimal</code> is <code>rhs</code>,
+  * and the context is <code>new MathContext(0, MathContext.PLAIN)</code>.
+  * <p>
+  * The parameter is the power to which the <code>this</code> will be
+  * raised; it must be in the range 0 through 999999999, and must
+  * have a decimal part of zero.  Note that these restrictions may be
+  * removed in the future, so they should not be used as a test for a
+  * whole number.
+  * <p>
+  * In addition