ICU-8291 Merge some important fixes done after 4.4 back to maint-4-4. #7201 (r28825), #7480 (r27945), #7690 (r28106) and #7880 (r28493).

X-SVN-Rev: 29415
diff --git a/main/classes/core/src/com/ibm/icu/impl/DateNumberFormat.java b/main/classes/core/src/com/ibm/icu/impl/DateNumberFormat.java
index a6e2b82..e2abb77 100644
--- a/main/classes/core/src/com/ibm/icu/impl/DateNumberFormat.java
+++ b/main/classes/core/src/com/ibm/icu/impl/DateNumberFormat.java
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-*   Copyright (C) 2007-2009, International Business Machines
+*   Copyright (C) 2007-2011, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 */
@@ -36,7 +36,7 @@
 
     private int maxIntDigits;
     private int minIntDigits;
- 
+
     public DateNumberFormat(ULocale loc, char zeroDigitIn) {
         initialize(loc,zeroDigitIn);
     }
@@ -152,6 +152,8 @@
     /*
      * Note: This method only parse integer numbers which can be represented by long
      */
+    private static final long PARSE_THRESHOLD = 922337203685477579L; // (Long.MAX_VALUE / 10) - 1
+
     public Number parse(String text, ParsePosition parsePosition) {
         long num = 0;
         boolean sawNumber = false;
@@ -170,7 +172,7 @@
                 if (digit < 0 || 9 < digit) {
                     digit = UCharacter.digit(ch);
                 }
-                if (0 <= digit && digit <= 9) {
+                if (0 <= digit && digit <= 9 && num < PARSE_THRESHOLD) {
                     sawNumber = true;
                     num = num * 10 + digit;
                 } else {
diff --git a/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java b/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java
index c2d3b38..71e2b52 100644
--- a/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java
+++ b/main/classes/core/src/com/ibm/icu/impl/OlsonTimeZone.java
@@ -1,6 +1,6 @@
  /*
   *******************************************************************************
-  * Copyright (C) 2005-2010, International Business Machines Corporation and         *
+  * Copyright (C) 2005-2011, International Business Machines Corporation and         *
   * others. All Rights Reserved.                                                *
   *******************************************************************************
   */
@@ -477,26 +477,30 @@
             // Post-32bit transition data is optional
         }
 
-        transitionTimes64 = new long[transitionCount];
-        int idx = 0;
-        if (transPre32 != null) {
-            for (int i = 0; i < transPre32.length / 2; i++, idx++) {
-                transitionTimes64[idx] = 
-                    (((long)transPre32[i * 2]) & 0x00000000FFFFFFFFL) << 32
-                    | (((long)transPre32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
+        if (transitionCount > 0) {
+            transitionTimes64 = new long[transitionCount];
+            int idx = 0;
+            if (transPre32 != null) {
+                for (int i = 0; i < transPre32.length / 2; i++, idx++) {
+                    transitionTimes64[idx] = 
+                        (((long)transPre32[i * 2]) & 0x00000000FFFFFFFFL) << 32
+                        | (((long)transPre32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
+                }
             }
-        }
-        if (trans32 != null) {
-            for (int i = 0; i < trans32.length; i++, idx++) {
-                transitionTimes64[idx] = (long)trans32[i];
+            if (trans32 != null) {
+                for (int i = 0; i < trans32.length; i++, idx++) {
+                    transitionTimes64[idx] = (long)trans32[i];
+                }
             }
-        }
-        if (transPost32 != null) {
-            for (int i = 0; i < transPost32.length / 2; i++, idx++) {
-                transitionTimes64[idx] = 
-                    (((long)transPost32[i * 2]) & 0x00000000FFFFFFFFL) << 32
-                    | (((long)transPost32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
+            if (transPost32 != null) {
+                for (int i = 0; i < transPost32.length / 2; i++, idx++) {
+                    transitionTimes64[idx] = 
+                        (((long)transPost32[i * 2]) & 0x00000000FFFFFFFFL) << 32
+                        | (((long)transPost32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
+                }
             }
+        } else {
+            transitionTimes64 = null;
         }
 
         // Type offsets list must be of even size, with size >= 2
@@ -508,13 +512,14 @@
         typeCount = typeOffsets.length / 2;
 
         // Type map data must be of the same size as the transition count
-        typeMapData = null;
         if (transitionCount > 0) {
             r = res.get("typeMap");
             typeMapData = r.getBinary(null);
             if (typeMapData.length != transitionCount) {
                 throw new IllegalArgumentException("Invalid Format");
             }
+        } else {
+            typeMapData = null;
         }
 
         // Process final rule and data, if any
@@ -727,6 +732,18 @@
         } else {
             buf.append("null");
         }
+        buf.append(",typeMapData=");
+        if (typeMapData != null) {
+            buf.append('[');
+            for (int i = 0; i < typeMapData.length; ++i) {
+                if (i > 0) {
+                    buf.append(',');
+                }
+                buf.append(Byte.toString(typeMapData[i]));
+            }
+        } else {
+            buf.append("null");
+        }
         buf.append(",finalStartYear=" + finalStartYear);
         buf.append(",finalStartMillis=" + finalStartMillis);
         buf.append(",finalZone=" + finalZone);
@@ -754,14 +771,14 @@
      * Offset from GMT in seconds for each type.
      * Length is equal to typeCount
      */
-    private int[] typeOffsets; // alias into res; do not delete
+    private int[] typeOffsets;
 
     /**
      * Type description data, consisting of transitionCount uint8_t
      * type indices (from 0..typeCount-1).
      * Length is equal to transitionCount
      */
-    private byte[] typeMapData; // alias into res; do not delete
+    private byte[] typeMapData;
 
     /**
      * For year >= finalStartYear, the finalZone will be used.
@@ -819,15 +836,19 @@
                    Double.doubleToLongBits(finalStartMillis)+
                    (finalZone == null ? 0 : finalZone.hashCode()) + 
                    super.hashCode());
-        for(int i=0; i<transitionTimes64.length; i++){
-            ret+=transitionTimes64[i]^(transitionTimes64[i]>>>8);
+        if (transitionTimes64 != null) {
+            for(int i=0; i<transitionTimes64.length; i++){
+                ret+=transitionTimes64[i]^(transitionTimes64[i]>>>8);
+            }
         }
         for(int i=0; i<typeOffsets.length; i++){
             ret+=typeOffsets[i]^(typeOffsets[i]>>>8);
         }
-        for(int i=0; i<typeMapData.length; i++){
-            ret+=typeMapData[i] & 0xFF;
-        } 
+        if (typeMapData != null) {
+            for(int i=0; i<typeMapData.length; i++){
+                ret+=typeMapData[i] & 0xFF;
+            } 
+        }
         return ret;
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/util/VersionInfo.java b/main/classes/core/src/com/ibm/icu/util/VersionInfo.java
index 8f200aa..681fa84 100644
--- a/main/classes/core/src/com/ibm/icu/util/VersionInfo.java
+++ b/main/classes/core/src/com/ibm/icu/util/VersionInfo.java
@@ -7,7 +7,7 @@
 
 package com.ibm.icu.util;
 
-import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Class to store version numbers of the form major.minor.milli.micro.
@@ -238,7 +238,10 @@
         VersionInfo  result  = MAP_.get(key);
         if (result == null) {
             result = new VersionInfo(version);
-            MAP_.put(key, result);
+            VersionInfo tmpvi = MAP_.putIfAbsent(key, result);
+            if (tmpvi != null) {
+                result = tmpvi;
+            }
         }
         return result;
     }
@@ -429,7 +432,7 @@
     /**
      * Map of singletons
      */
-    private static final HashMap<Integer, VersionInfo> MAP_ = new HashMap<Integer, VersionInfo>();
+    private static final ConcurrentHashMap<Integer, VersionInfo> MAP_ = new ConcurrentHashMap<Integer, VersionInfo>();
     /**
      * Last byte mask
      */
diff --git a/main/classes/translit/src/com/ibm/icu/text/BreakTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/BreakTransliterator.java
index b0950eb..bec670f 100644
--- a/main/classes/translit/src/com/ibm/icu/text/BreakTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/BreakTransliterator.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2010, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -70,7 +70,7 @@
         | (1<<Character.NON_SPACING_MARK)
         | (1<<Character.ENCLOSING_MARK)
         ;
-    protected void handleTransliterate(Replaceable text, Position pos, boolean incremental) {
+    protected synchronized void handleTransliterate(Replaceable text, Position pos, boolean incremental) {
         boundaryCount = 0;
         int boundary = 0;
         getBreakIterator(); // Lazy-create it if necessary
diff --git a/main/classes/translit/src/com/ibm/icu/text/CaseFoldTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/CaseFoldTransliterator.java
index 3750522..34e0aad 100644
--- a/main/classes/translit/src/com/ibm/icu/text/CaseFoldTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/CaseFoldTransliterator.java
@@ -1,8 +1,8 @@
 /*
- *******************************************************************************
- * Copyright (C) 2009, Google, International Business Machines Corporation     *
- * and others. All Rights Reserved.                                            *
- *******************************************************************************
+ ********************************************************************************
+ * Copyright (C) 2009-2011, Google, International Business Machines Corporation *
+ * and others. All Rights Reserved.                                             *
+ ********************************************************************************
  */
 package com.ibm.icu.text;
 
@@ -58,7 +58,7 @@
     /**
      * Implements {@link Transliterator#handleTransliterate}.
      */
-    protected void handleTransliterate(Replaceable text,
+    protected synchronized void handleTransliterate(Replaceable text,
                                        Position offsets, boolean isIncremental) {
         if(csp==null) {
             return;
diff --git a/main/classes/translit/src/com/ibm/icu/text/LowercaseTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/LowercaseTransliterator.java
index 18a2fef..26c55dc 100644
--- a/main/classes/translit/src/com/ibm/icu/text/LowercaseTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/LowercaseTransliterator.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2010, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -65,7 +65,7 @@
     /**
      * Implements {@link Transliterator#handleTransliterate}.
      */
-    protected void handleTransliterate(Replaceable text,
+    protected synchronized void handleTransliterate(Replaceable text,
                                        Position offsets, boolean isIncremental) {
         if(csp==null) {
             return;
diff --git a/main/classes/translit/src/com/ibm/icu/text/NameUnicodeTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/NameUnicodeTransliterator.java
index 1c2621a..7465a02 100644
--- a/main/classes/translit/src/com/ibm/icu/text/NameUnicodeTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/NameUnicodeTransliterator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2010, International Business Machines Corporation and
+ * Copyright (C) 1996-2011, International Business Machines Corporation and
  * others. All Rights Reserved.
  */
 package com.ibm.icu.text;
@@ -14,9 +14,6 @@
  */
 class NameUnicodeTransliterator extends Transliterator {
 
-    char openDelimiter;
-    char closeDelimiter;
-
     static final String _ID = "Name-Any";
 
     static final String OPEN_PAT    = "\\N~{~";
diff --git a/main/classes/translit/src/com/ibm/icu/text/RuleBasedTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/RuleBasedTransliterator.java
index ed2fc9f..415732d 100644
--- a/main/classes/translit/src/com/ibm/icu/text/RuleBasedTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/RuleBasedTransliterator.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2009, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -328,7 +328,7 @@
      * @internal
      * @deprecated This API is ICU internal only.
      */
-    protected synchronized void handleTransliterate(Replaceable text,
+    protected void handleTransliterate(Replaceable text,
                                        Position index, boolean incremental) {
         /* We keep start and limit fixed the entire time,
          * relative to the text -- limit may move numerically if text is
@@ -354,16 +354,18 @@
          * number of characters n, unless n is so large that 16n exceeds a
          * uint32_t.
          */
-        int loopCount = 0;
-        int loopLimit = (index.limit - index.start) << 4;
-        if (loopLimit < 0) {
-            loopLimit = 0x7FFFFFFF;
-        }
+        synchronized(data)  {
+            int loopCount = 0;
+            int loopLimit = (index.limit - index.start) << 4;
+            if (loopLimit < 0) {
+                loopLimit = 0x7FFFFFFF;
+            }
 
-        while (index.start < index.limit &&
-               loopCount <= loopLimit &&
-               data.ruleSet.transliterate(text, index, incremental)) {
-            ++loopCount;
+            while (index.start < index.limit &&
+                    loopCount <= loopLimit &&
+                    data.ruleSet.transliterate(text, index, incremental)) {
+                ++loopCount;
+            }
         }
     }
 
diff --git a/main/classes/translit/src/com/ibm/icu/text/TitlecaseTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/TitlecaseTransliterator.java
index f231f20..dae5cf3 100644
--- a/main/classes/translit/src/com/ibm/icu/text/TitlecaseTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/TitlecaseTransliterator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2010, International Business Machines Corporation and
+ * Copyright (C) 1996-2011, International Business Machines Corporation and
  * others. All Rights Reserved.
  *
  */
@@ -63,7 +63,7 @@
     /**
      * Implements {@link Transliterator#handleTransliterate}.
      */
-    protected void handleTransliterate(Replaceable text,
+    protected synchronized void handleTransliterate(Replaceable text,
                                        Position offsets, boolean isIncremental) {
         // TODO reimplement, see ustrcase.c
         // using a real word break iterator
diff --git a/main/classes/translit/src/com/ibm/icu/text/UppercaseTransliterator.java b/main/classes/translit/src/com/ibm/icu/text/UppercaseTransliterator.java
index 1417811..8e2ed63 100644
--- a/main/classes/translit/src/com/ibm/icu/text/UppercaseTransliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/UppercaseTransliterator.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2010, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -61,7 +61,7 @@
     /**
      * Implements {@link Transliterator#handleTransliterate}.
      */
-    protected void handleTransliterate(Replaceable text,
+    protected synchronized void handleTransliterate(Replaceable text,
                                        Position offsets, boolean isIncremental) {
     if(csp==null) {
         return;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java b/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java
index b5cdcea..1946403 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2001-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2001-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -3701,4 +3701,20 @@
         };
         expect(DATA, new Locale("en", "", ""));
     }
+
+    /*
+     * Test case for very long contiguous numeric patterns (ticket#7480)
+     */
+    public void TestLongContiguousNumericPattern() {
+        String DATA[] = {
+                "yyyy-MM-dd HH:mm:ss.SSS",
+
+                "yyyyMMddHHmmssSSSSSS", "fp", "2010-04-16 12:23:34.456",
+                "20100416122334456000", "2010-04-16 12:23:34.456",
+
+                "yyyyyyMMddHHHHmmmmssssSSSSSS", "fp", "2010-04-16 12:23:34.456",
+                "0020100416001200230034456000", "2010-04-16 12:23:34.456",
+        };
+            expect(DATA, new Locale("en", "", ""));
+    }
 }
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java b/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java
index 514a4e1..8edf9aa 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/timezone/TimeZoneTest.java
@@ -1,6 +1,6 @@
 /**
  *******************************************************************************
- * Copyright (C) 2000-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2000-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -1622,6 +1622,27 @@
             logln("Note: Errors could be the result of changes to zoneStrings locale data");
         }
     }
+
+    /*
+     * Test case for hashCode problem reported by ticket#7690 OlsonTimeZone.hashCode() throws NPE.
+     */
+    public void TestHashCode() {
+        String[] ids = TimeZone.getAvailableIDs();
+
+        for (String id: ids) {
+            TimeZone tz1 = TimeZone.getTimeZone(id);
+            TimeZone tz2 = TimeZone.getTimeZone(id);
+
+            // hash code are same for the same time zone
+            if (tz1.hashCode() != tz2.hashCode()) {
+                errln("Fail: Two time zone instances for " + id + " have different hash values.");
+            }
+            // string representation should be also same
+            if (!tz1.toString().equals(tz2.toString())) {
+                errln("Fail: Two time zone instances for " + id + " have different toString() values.");
+            }
+        }
+    }
 }
 
 //eof
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/VersionInfoTest.java b/main/tests/core/src/com/ibm/icu/dev/test/util/VersionInfoTest.java
index 7a30723..5b9c611 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/VersionInfoTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/VersionInfoTest.java
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 1996-2009, International Business Machines Corporation and    *
+* Copyright (C) 1996-2011, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
@@ -342,4 +342,56 @@
         "2.1.255.0",
         "3.1.255.100"    
     };
+
+    /*
+     * Test case for multi-threading problem reported by ticket#7880
+     */
+    public void TestMultiThread() {
+        final int numThreads = 20;
+        GetInstanceWorker[] workers = new GetInstanceWorker[numThreads];
+        VersionInfo[][] results = new VersionInfo[numThreads][255];
+
+        // Create workers
+        for (int i = 0; i < workers.length; i++) {
+            workers[i] = new GetInstanceWorker(i, results[i]);
+        }
+
+        // Start workers
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].start();
+        }
+
+        // Wait for the completion
+        for (int i = 0; i < workers.length; i++) {
+            try {
+                workers[i].join();
+            } catch (InterruptedException e) {
+                errln("A problem in thread execution. " + e.getMessage());
+            }
+        }
+
+        // Check if singleton for each
+        for (int i = 1; i < results.length; i++) {
+            for (int j = 0; j < results[0].length; j++) {
+                if (results[0][j] != results[i][j]) {
+                    errln("Different instance at index " + j + " Thread#" + i);
+                }
+            }
+        }
+    }
+
+    private class GetInstanceWorker extends Thread {
+        private VersionInfo[] results;
+
+        GetInstanceWorker(int serialNumber, VersionInfo[] results) {
+            super("GetInstnaceWorker#" + serialNumber);
+            this.results = results;
+        }
+
+        public void run() {
+            for (int i = 0; i < results.length; i++) {
+                results[i] = VersionInfo.getInstance(i);
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/main/tests/translit/src/com/ibm/icu/dev/test/translit/TestAll.java b/main/tests/translit/src/com/ibm/icu/dev/test/translit/TestAll.java
index 8301230..b32e48d 100644
--- a/main/tests/translit/src/com/ibm/icu/dev/test/translit/TestAll.java
+++ b/main/tests/translit/src/com/ibm/icu/dev/test/translit/TestAll.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2009, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -28,6 +28,7 @@
                 "TransliteratorTest",
                 "RegexUtilitiesTest",
                 "UnicodeMapTest",
+                "ThreadTest"
         });
     }
 
diff --git a/main/tests/translit/src/com/ibm/icu/dev/test/translit/ThreadTest.java b/main/tests/translit/src/com/ibm/icu/dev/test/translit/ThreadTest.java
new file mode 100644
index 0000000..6d15f20
--- /dev/null
+++ b/main/tests/translit/src/com/ibm/icu/dev/test/translit/ThreadTest.java
@@ -0,0 +1,66 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2010-2011, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.dev.test.translit;
+
+import java.util.ArrayList;
+import com.ibm.icu.dev.test.TestFmwk;
+import com.ibm.icu.text.Transliterator;
+
+// Test for ICU Ticket #7201.  With threading bugs in RuleBasedTransliterator, this
+//   test would reliably crash.
+
+public class ThreadTest extends TransliteratorTest {
+    public static void main(String[] args) throws Exception {
+        new ThreadTest().run(args);
+    }
+    
+    private ArrayList<Worker> threads = new ArrayList<Worker>();
+    private int iterationCount = 100000;
+    
+    public void TestThreads()  {
+        if (getInclusion() >= 9) {
+            // Exhaustive test.  Run longer.
+            iterationCount = 1000000;
+        }
+        
+        for (int i = 0; i < 8; i++) {
+            Worker thread = new Worker();
+            threads.add(thread);
+            thread.start();
+        }
+        long expectedCount = 0;
+        for (Worker thread: threads) {
+            try {
+                thread.join();
+                if (expectedCount == 0) {
+                    expectedCount = thread.count;
+                } else {
+                    if (expectedCount != thread.count) {
+                        errln("Threads gave differing results.");
+                    }
+                }
+            } catch (InterruptedException e) {
+                errln(e.toString());
+            }
+        }
+    }
+    
+    private static final String [] WORDS = {"edgar", "allen", "poe"};
+   
+    private class Worker extends Thread {   
+        public long count = 0;
+        public void run() {
+            Transliterator tx = Transliterator.getInstance("Latin-Thai");        
+            for (int loop = 0; loop < iterationCount; loop++) {
+                for (String s : WORDS) {
+                    count += tx.transliterate(s).length();
+                }                
+            }
+        }
+    }
+
+}