ICU-7919 Synchronized eclipse37 with maint-4-4 r30556. Lock the eclipse plug-in build version string.

X-SVN-Rev: 30564
diff --git a/build.properties b/build.properties
index 8fec146..b4182a3 100644
--- a/build.properties
+++ b/build.properties
@@ -1,6 +1,6 @@
 #*******************************************************************************

-#* Copyright (C) 2009-2010, International Business Machines Corporation and    *

+#* Copyright (C) 2009-2011, International Business Machines Corporation and    *

 #* others. All Rights Reserved.                                                *

 #*******************************************************************************

-api.report.version = 442

+api.report.version = 4422

 api.report.prev.version = 421

diff --git a/eclipse-build/build.properties b/eclipse-build/build.properties
index 0563c56..f03aa69 100644
--- a/eclipse-build/build.properties
+++ b/eclipse-build/build.properties
@@ -5,4 +5,4 @@
 icu4j.plugin.impl.version.string=4.4.2
 copyright.eclipse=Licensed Materials - Property of IBM \n (C) Copyright IBM Corp. 2000, 2011. All Rights Reserved. \n IBM is a registered trademark of IBM Corp.
 icu4j.data.version.number=44
-icu4j.eclipse.build.version.string=4.4.2.v20110208
+icu4j.eclipse.build.version.string=4.4.2.v20110823
diff --git a/main/classes/charset/src/com/ibm/icu/charset/CharsetUTF7.java b/main/classes/charset/src/com/ibm/icu/charset/CharsetUTF7.java
index 9447bdb..3c1a72a 100644
--- a/main/classes/charset/src/com/ibm/icu/charset/CharsetUTF7.java
+++ b/main/classes/charset/src/com/ibm/icu/charset/CharsetUTF7.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2009, International Business Machines Corporation and         *
+ * Copyright (C) 2007-2011, International Business Machines Corporation and         *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -276,12 +276,52 @@
                             b=(char)source.get();
                             sourceArrayIndex++;
                             toUBytesArray[byteIndex++]=(byte)b;
-                            if ((!useIMAP && b>=126) || (useIMAP && b>0x7e)) {
-                                /* illegal - test other illegal US-ASCII values by base64Value==-3 */
+                            base64Value = -3; /* initialize as illegal */
+                            if ((!useIMAP && (b>=126 || (base64Value=FROM_BASE_64[b])==-3 || base64Value==-1)) || (useIMAP && b>0x7e)) {
+                                /* either
+                                 * base64Value==-1 for any legal character except base64 and minus sign, or
+                                 * base64Value==-3 for illegal characters:
+                                 * 1. In either case, leave Unicode mode.
+                                 * 2.1. If we ended with an incomplete UChar or none after the +, then
+                                 *      generate an error for the preceding erroneous sequence and deal with
+                                 *      the current (possibly illegal) character next time through.
+                                 * 2.2. Else the current char comes after a complete UChar, which was already
+                                 *      pushed to the output buf, so:
+                                 * 2.2.1. If the current char is legal, just save it for processing next time.
+                                 *        It may be for example, a plus which we need to deal with in direct mode.
+                                 * 2.2.2. Else if the current char is illegal, we might as well deal with it here.
+                                 */
                                 inDirectMode=1;
-                                cr=CoderResult.malformedForLength(sourceArrayIndex);
-                                break directMode;
-                            } else if (((base64Value=FROM_BASE_64[b])>=0 && !useIMAP) || ((base64Value=FROM_BASE64_IMAP(b))>=0) && useIMAP) {
+                                
+                                if(base64Counter==-1) {
+                                    /* illegal: + immediately followed by something other than base64 or minus sign */
+                                    /* include the plus sign in the reported sequence, but not the subsequent char */
+                                    source.position(source.position() -1);
+                                    toUBytesArray[0]=PLUS;
+                                    byteIndex=1;
+                                    cr=CoderResult.malformedForLength(sourceArrayIndex);
+                                    break directMode;
+                                } else if(bits!=0) {
+                                    /* bits are illegally left over, a UChar is incomplete */
+                                    /* don't include current char (legal or illegal) in error seq */
+                                    source.position(source.position() -1);
+                                    --byteIndex;
+                                    cr=CoderResult.malformedForLength(sourceArrayIndex);
+                                    break directMode;
+                                } else {
+                                    /* previous UChar was complete */
+                                    if(base64Value==-3) {
+                                        /* current character is illegal, deal with it here */
+                                        cr=CoderResult.malformedForLength(sourceArrayIndex);
+                                        break directMode;
+                                    } else {
+                                        /* un-read the current character in case it is a plus sign */
+                                        source.position(source.position() -1);
+                                        sourceIndex=nextSourceIndex-1;
+                                        continue directMode;
+                                    }
+                                }
+                            } else if ((!useIMAP && (base64Value=FROM_BASE_64[b])>=0) || (useIMAP && (base64Value=FROM_BASE64_IMAP(b))>=0)) {
                                 /* collect base64 bytes */
                                 switch (base64Counter) {
                                 case -1: /* -1 is immediately after the + */
@@ -356,7 +396,7 @@
                                     /* will never occur */
                                     //break;                                                           
                                 }//end of switch
-                            } else if (base64Value==-2) {
+                            } else if (!useIMAP || (useIMAP && base64Value==-2)) {
                                 /* minus sign terminates the base64 sequence */
                                 inDirectMode=1;
                                 if (base64Counter==-1) {
@@ -375,30 +415,8 @@
                                 }
                                 sourceIndex=nextSourceIndex;
                                 continue directMode;
-                            } else if (!useIMAP && base64Value==-1) { /* for any legal character except base64 and minus sign */
-                                /* leave the Unicode Mode */
-                                inDirectMode=1;
+                            } else if (useIMAP) { 
                                 if (base64Counter==-1) {
-                                    /* illegal:  + immediately followed by something other than base64 minus sign */
-                                    /* include the plus sign in the reported sequence */
-                                    --sourceIndex;
-                                    toUBytesArray[0]=PLUS;
-                                    toUBytesArray[1]=(byte)b;
-                                    byteIndex=2;
-                                    cr=CoderResult.malformedForLength(sourceArrayIndex);
-                                    break;
-                                } else if (bits==0) {
-                                    /* un-read the character in case it is a plus sign */
-                                    source.position(--sourceArrayIndex);
-                                    sourceIndex=nextSourceIndex - 1;
-                                    continue directMode;
-                                } else {
-                                    /* bits are illegally left over, a unicode character is incomplete */
-                                    cr=CoderResult.malformedForLength(sourceArrayIndex);
-                                    break;
-                                }
-                            } else { 
-                                if (useIMAP && base64Counter==-1) {
                                     // illegal: & immediately followed by something other than base64 or minus sign
                                     // include the ampersand in the reported sequence
                                     --sourceIndex;
@@ -718,17 +736,16 @@
                             cr=CoderResult.OVERFLOW;
                         }
                     }
-                    if (useIMAP) {
-                        /* IMAP: need to terminate with a minus */
-                        if (target.hasRemaining()) {
-                            target.put(MINUS);
-                            if (offsets!=null) {
-                                offsets.put(sourceIndex - 1);
-                            }
-                        } else {
-                            errorBuffer[errorBufferLength++]=MINUS;
-                            cr=CoderResult.OVERFLOW;
+                    
+                    /* need to terminate with a minus */
+                    if (target.hasRemaining()) {
+                        target.put(MINUS);
+                        if (offsets!=null) {
+                            offsets.put(sourceIndex - 1);
                         }
+                    } else {
+                        errorBuffer[errorBufferLength++]=MINUS;
+                        cr=CoderResult.OVERFLOW;
                     }
                 }
                 /*reset the state for the next conversion */
diff --git a/main/classes/collate/src/com/ibm/icu/text/CollationElementIterator.java b/main/classes/collate/src/com/ibm/icu/text/CollationElementIterator.java
index c331af7..c423a97 100644
--- a/main/classes/collate/src/com/ibm/icu/text/CollationElementIterator.java
+++ b/main/classes/collate/src/com/ibm/icu/text/CollationElementIterator.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.                                                *
 *******************************************************************************
 *
@@ -1652,10 +1652,19 @@
                 // Source string char was not in contraction table.
                 // Unless it is a discontiguous contraction, we are done
                 int miss = ch;
-                if(UTF16.isLeadSurrogate(ch)) { // in order to do the proper detection, we
-                    // need to see if we're dealing with a supplementary
-                    miss = UCharacterProperty.getRawSupplementary(ch, (char) nextChar());
-                  }
+                // ticket 8484 - porting changes from C for 6101
+                // We test whether the next two char are surrogate pairs.
+                // This test is done if the iterator is not in the end.
+                // If there is no surrogate pair, the iterator
+                // goes back one if needed. 
+                if(UTF16.isLeadSurrogate(ch) && !isEnd()) {
+                    char surrNextChar = (char)nextChar();
+                    if (UTF16.isTrailSurrogate(surrNextChar)) {
+                        miss = UCharacterProperty.getRawSupplementary(ch, surrNextChar);
+                    } else {
+                        previousChar();
+                    }
+                }
                 int sCC;
                 if (maxCC == 0 || (sCC = getCombiningClass(miss)) == 0
                     || sCC > maxCC || (allSame != 0 && sCC == maxCC) ||
diff --git a/main/classes/collate/src/com/ibm/icu/text/Collator.java b/main/classes/collate/src/com/ibm/icu/text/Collator.java
index 5447181..920b14f 100644
--- a/main/classes/collate/src/com/ibm/icu/text/Collator.java
+++ b/main/classes/collate/src/com/ibm/icu/text/Collator.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.                                                *
 *******************************************************************************
 */
@@ -17,6 +17,7 @@
 import com.ibm.icu.impl.ICUDebug;
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.VersionInfo;
 
@@ -767,24 +768,26 @@
 
     /**
      * {@icu} Returns the name of the collator for the objectLocale, localized for the
-     * current locale.
+     * default <code>DISPLAY</code> locale.
      * @param objectLocale the locale of the collator
      * @return the display name
+     * @see com.ibm.icu.util.ULocale.Category#DISPLAY
      * @stable ICU 2.6
      */
     static public String getDisplayName(Locale objectLocale) {
-        return getShim().getDisplayName(ULocale.forLocale(objectLocale), ULocale.getDefault());
+        return getShim().getDisplayName(ULocale.forLocale(objectLocale), ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
      * {@icu} Returns the name of the collator for the objectLocale, localized for the
-     * current locale.
+     * default <code>DISPLAY</code> locale.
      * @param objectLocale the locale of the collator
      * @return the display name
+     * @see com.ibm.icu.util.ULocale.Category#DISPLAY
      * @stable ICU 3.2
      */
     static public String getDisplayName(ULocale objectLocale) {
-        return getShim().getDisplayName(objectLocale, ULocale.getDefault());
+        return getShim().getDisplayName(objectLocale, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/classes/collate/src/com/ibm/icu/text/CollatorServiceShim.java b/main/classes/collate/src/com/ibm/icu/text/CollatorServiceShim.java
index 6d08c82..26282b6 100644
--- a/main/classes/collate/src/com/ibm/icu/text/CollatorServiceShim.java
+++ b/main/classes/collate/src/com/ibm/icu/text/CollatorServiceShim.java
@@ -1,6 +1,6 @@
 /**
 *******************************************************************************
-* Copyright (C) 2003-2009, International Business Machines Corporation and         *
+* Copyright (C) 2003-2011, International Business Machines Corporation and         *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
@@ -12,9 +12,9 @@
 import java.util.Set;
 
 import com.ibm.icu.impl.ICULocaleService;
+import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.impl.ICUService;
-import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
 import com.ibm.icu.impl.ICUService.Factory;
 import com.ibm.icu.text.Collator.CollatorFactory;
 import com.ibm.icu.util.ULocale;
diff --git a/main/classes/collate/src/com/ibm/icu/text/RuleBasedCollator.java b/main/classes/collate/src/com/ibm/icu/text/RuleBasedCollator.java
index 98868e8..7b78d28 100644
--- a/main/classes/collate/src/com/ibm/icu/text/RuleBasedCollator.java
+++ b/main/classes/collate/src/com/ibm/icu/text/RuleBasedCollator.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.                                                *
 *******************************************************************************
 */
@@ -3957,7 +3957,7 @@
         }
 
         // Set the compression values
-        int total3 = m_top3_ - COMMON_BOTTOM_3_ - 1;
+        int total3 = m_top3_ - m_bottom3_ - 1;
         // we multilply double with int, but need only int
         m_topCount3_ = (int)(PROPORTION_3_ * total3);
         m_bottomCount3_ = total3 - m_topCount3_;
diff --git a/main/classes/core/src/com/ibm/icu/impl/ICUService.java b/main/classes/core/src/com/ibm/icu/impl/ICUService.java
index a0c1815..486a06b 100644
--- a/main/classes/core/src/com/ibm/icu/impl/ICUService.java
+++ b/main/classes/core/src/com/ibm/icu/impl/ICUService.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.                                                *
  *******************************************************************************
  */
@@ -17,12 +17,13 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.Map.Entry;
 
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * <p>A Service provides access to service objects that implement a
@@ -625,7 +626,7 @@
      * uses the current default locale.
      */
     public String getDisplayName(String id) {
-        return getDisplayName(id, ULocale.getDefault());
+        return getDisplayName(id, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
@@ -657,7 +658,7 @@
      * the comparator, and null for the matchID.
      */
     public SortedMap<String, String> getDisplayNames() {
-        ULocale locale = ULocale.getDefault();
+        ULocale locale = ULocale.getDefault(Category.DISPLAY);
         return getDisplayNames(locale, null, null);
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_da_DK.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_da_DK.java
index ed59e44..3aa5fa8 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_da_DK.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_da_DK.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_da_DK extends ListResourceBundle
 {
     static private final Holiday[] fHolidays = {
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_AT.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_AT.java
index 3e6f714..a5d3108 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_AT.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_AT.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_de_AT extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_DE.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_DE.java
index a39feee..28b8572 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_DE.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_de_DE.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_de_DE extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_el_GR.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_el_GR.java
index 9e1fabb..3c4f18b 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_el_GR.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_el_GR.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_el_GR extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_CA.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_CA.java
index de9f803..32eb6ae 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_CA.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_CA.java
@@ -1,16 +1,18 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_en_CA extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_GB.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_GB.java
index 9a71420..12efea0 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_GB.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_GB.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_en_GB extends ListResourceBundle
 {
     static private final Holiday[] fHolidays = {
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_US.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_US.java
index 11d1531..76f3637 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_US.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_en_US.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_en_US extends ListResourceBundle
 {
     static private final Holiday[] fHolidays = {
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_es_MX.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_es_MX.java
index dc52fcf..b234f89 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_es_MX.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_es_MX.java
@@ -1,16 +1,18 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_es_MX extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_CA.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_CA.java
index fd34777..1ded00c 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_CA.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_CA.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_fr_CA extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         new SimpleHoliday(Calendar.JANUARY,    1,  0,                  "New Year's Day"),
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_FR.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_FR.java
index 244a9b6..335ea56 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_FR.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_fr_FR.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_fr_FR extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_it_IT.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_it_IT.java
index 919648f..7ab5b3d 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_it_IT.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_it_IT.java
@@ -1,16 +1,19 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.EasterHoliday;
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_it_IT extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         SimpleHoliday.NEW_YEARS_DAY,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_iw_IL.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_iw_IL.java
index 7d60b08..2295e63 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_iw_IL.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_iw_IL.java
@@ -1,15 +1,17 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.HebrewHoliday;
+import com.ibm.icu.util.Holiday;
+
 public class HolidayBundle_iw_IL extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         HebrewHoliday.ROSH_HASHANAH,
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_ja_JP.java b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_ja_JP.java
index e7ef8ff..4022689 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_ja_JP.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/HolidayBundle_ja_JP.java
@@ -1,16 +1,18 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2005, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import com.ibm.icu.util.*;
 import java.util.Calendar;
 import java.util.ListResourceBundle;
 
+import com.ibm.icu.util.Holiday;
+import com.ibm.icu.util.SimpleHoliday;
+
 public class HolidayBundle_ja_JP extends ListResourceBundle {
     static private final Holiday[] fHolidays = {
         new SimpleHoliday(Calendar.FEBRUARY,  11,  0,    "National Foundation Day"),
diff --git a/main/classes/core/src/com/ibm/icu/impl/data/ResourceReader.java b/main/classes/core/src/com/ibm/icu/impl/data/ResourceReader.java
index 63fd084..c953f9a 100644
--- a/main/classes/core/src/com/ibm/icu/impl/data/ResourceReader.java
+++ b/main/classes/core/src/com/ibm/icu/impl/data/ResourceReader.java
@@ -1,13 +1,17 @@
 /**
  *******************************************************************************
- * Copyright (C) 2001-2009, International Business Machines Corporation and    *
+ * Copyright (C) 2001-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 
 package com.ibm.icu.impl.data;
 
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
 
 import com.ibm.icu.impl.ICUData;
 import com.ibm.icu.impl.Utility;
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodBuilderFactory.java b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodBuilderFactory.java
index 027aa8d..fa53269 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodBuilderFactory.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodBuilderFactory.java
@@ -1,18 +1,18 @@
 /*
 ******************************************************************************
-* Copyright (C) 2007-2009, International Business Machines Corporation and   *
+* Copyright (C) 2007-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
 
 package com.ibm.icu.impl.duration;
 
+import java.util.TimeZone;
+
 import com.ibm.icu.impl.duration.impl.DataRecord;
 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
 import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
 
-import java.util.TimeZone;
-
 /**
  * Default implementation of PeriodBuilderFactory.  This creates builders that
  * use approximate durations.
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java
index 6ec213d..f259ff4 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java
@@ -1,6 +1,6 @@
 /*
 ******************************************************************************
-* Copyright (C) 2007-2008, International Business Machines Corporation and   *
+* Copyright (C) 2007-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
@@ -8,8 +8,11 @@
 package com.ibm.icu.impl.duration;
 
 import com.ibm.icu.impl.duration.BasicPeriodFormatterFactory.Customizations;
-
-import com.ibm.icu.impl.duration.impl.DataRecord.*;
+import com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
+import com.ibm.icu.impl.duration.impl.DataRecord.EMilliSupport;
+import com.ibm.icu.impl.duration.impl.DataRecord.ESeparatorVariant;
+import com.ibm.icu.impl.duration.impl.DataRecord.ETimeDirection;
+import com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit;
 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
 
 /**
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterFactory.java b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterFactory.java
index 91a9c3e..cd9d830 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterFactory.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterFactory.java
@@ -1,20 +1,19 @@
 /*
 ******************************************************************************
-* Copyright (C) 2007-2008, International Business Machines Corporation and   *
+* Copyright (C) 2007-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
 
 package com.ibm.icu.impl.duration;
 
-import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
-import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
+import java.util.Locale;
 
-import com.ibm.icu.impl.duration.impl.DataRecord.EUnitVariant;
 import com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
 import com.ibm.icu.impl.duration.impl.DataRecord.ESeparatorVariant;
-
-import java.util.Locale;
+import com.ibm.icu.impl.duration.impl.DataRecord.EUnitVariant;
+import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
+import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
 
 /**
  * An implementation of PeriodFormatterFactory that provides customization of
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterService.java b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterService.java
index 46c178f..149a541 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterService.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatterService.java
@@ -1,17 +1,17 @@
 /*
  ******************************************************************************
- * Copyright (C) 2007-2009, International Business Machines Corporation and   *
+ * Copyright (C) 2007-2011, International Business Machines Corporation and   *
  * others. All Rights Reserved.                                               *
  ******************************************************************************
  */
 
 package com.ibm.icu.impl.duration;
 
+import java.util.Collection;
+
 import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
 import com.ibm.icu.impl.duration.impl.ResourceBasedPeriodFormatterDataService;
 
-import java.util.Collection;
-
 /**
  * An implementation of PeriodFormatterService that constructs a
  * BasicPeriodFormatterFactory.
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/impl/PeriodFormatterData.java b/main/classes/core/src/com/ibm/icu/impl/duration/impl/PeriodFormatterData.java
index 7183990..85ff023 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/impl/PeriodFormatterData.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/impl/PeriodFormatterData.java
@@ -1,6 +1,6 @@
 /*
 ******************************************************************************
-* Copyright (C) 2009, International Business Machines Corporation and   *
+* Copyright (C) 2009-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
@@ -8,8 +8,17 @@
 package com.ibm.icu.impl.duration.impl;
 
 import com.ibm.icu.impl.duration.TimeUnit;
-
-import com.ibm.icu.impl.duration.impl.DataRecord.*;
+import com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
+import com.ibm.icu.impl.duration.impl.DataRecord.EDecimalHandling;
+import com.ibm.icu.impl.duration.impl.DataRecord.EFractionHandling;
+import com.ibm.icu.impl.duration.impl.DataRecord.EGender;
+import com.ibm.icu.impl.duration.impl.DataRecord.EHalfPlacement;
+import com.ibm.icu.impl.duration.impl.DataRecord.EHalfSupport;
+import com.ibm.icu.impl.duration.impl.DataRecord.ENumberSystem;
+import com.ibm.icu.impl.duration.impl.DataRecord.EPluralization;
+import com.ibm.icu.impl.duration.impl.DataRecord.EUnitVariant;
+import com.ibm.icu.impl.duration.impl.DataRecord.EZeroHandling;
+import com.ibm.icu.impl.duration.impl.DataRecord.ScopeData;
 
 
 /**
diff --git a/main/classes/core/src/com/ibm/icu/impl/duration/impl/YMDDateFormatter.java b/main/classes/core/src/com/ibm/icu/impl/duration/impl/YMDDateFormatter.java
index 6845e17..a906e5c 100644
--- a/main/classes/core/src/com/ibm/icu/impl/duration/impl/YMDDateFormatter.java
+++ b/main/classes/core/src/com/ibm/icu/impl/duration/impl/YMDDateFormatter.java
@@ -1,19 +1,19 @@
 /*
 ******************************************************************************
-* Copyright (C) 2007-2008, International Business Machines Corporation and   *
+* Copyright (C) 2007-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
 
 package com.ibm.icu.impl.duration.impl;
 
-import  com.ibm.icu.impl.duration.DateFormatter;
-
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
 
+import com.ibm.icu.impl.duration.DateFormatter;
+
 /**
  * A DateFormatter that formats the requested date fields.
  */
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/AsciiUtil.java b/main/classes/core/src/com/ibm/icu/impl/locale/AsciiUtil.java
index 78759fe..2f751d9 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/AsciiUtil.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/AsciiUtil.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -167,6 +167,9 @@
         }
 
         public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
             if (o instanceof CaseInsensitiveKey) {
                 return AsciiUtil.caseIgnoreMatch(_key, ((CaseInsensitiveKey)o)._key);
             }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/BaseLocale.java b/main/classes/core/src/com/ibm/icu/impl/locale/BaseLocale.java
index f7f0927..be24cff 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/BaseLocale.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/BaseLocale.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -12,6 +12,11 @@
 
     private static final boolean JDKIMPL = false;
 
+    public static final String SEP = "_";
+
+    private static final Cache CACHE = new Cache();
+    public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", "");
+
     private String _language = "";
     private String _script = "";
     private String _region = "";
@@ -19,11 +24,6 @@
 
     private transient volatile int _hash = 0;
 
-    private static final LocaleObjectCache<Key, BaseLocale> BASELOCALE_CACHE
-        = new LocaleObjectCache<Key, BaseLocale>();
-
-    public static final BaseLocale ROOT = BaseLocale.getInstance("", "", "", "");
-
     private BaseLocale(String language, String script, String region, String variant) {
         if (language != null) {
             _language = AsciiUtil.toLowerString(language).intern();
@@ -56,11 +56,7 @@
             }
         }
         Key key = new Key(language, script, region, variant);
-        BaseLocale baseLocale = BASELOCALE_CACHE.get(key);
-        if (baseLocale == null) {
-            baseLocale = new BaseLocale(language, script, region, variant);
-            baseLocale = BASELOCALE_CACHE.put(baseLocale.createKey(), baseLocale);
-        }
+        BaseLocale baseLocale = CACHE.get(key);
         return baseLocale;
     }
 
@@ -80,6 +76,21 @@
         return _variant;
     }
 
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof BaseLocale)) {
+            return false;
+        }
+        BaseLocale other = (BaseLocale)obj;
+        return hashCode() == other.hashCode()
+                && _language.equals(other._language)
+                && _script.equals(other._script)
+                && _region.equals(other._region)
+                && _variant.equals(other._variant);
+    }
+
     public String toString() {
         StringBuilder buf = new StringBuilder();
         if (_language.length() > 0) {
@@ -131,10 +142,6 @@
         return h;
     }
 
-    private Key createKey() {
-        return new Key(_language, _script, _region, _variant);
-    }
-
     private static class Key implements Comparable<Key> {
         private String _lang = "";
         private String _scrt = "";
@@ -217,5 +224,34 @@
             }
             return h;
         }
+
+        public static Key normalize(Key key) {
+            String lang = AsciiUtil.toLowerString(key._lang).intern();
+            String scrt = AsciiUtil.toTitleString(key._scrt).intern();
+            String regn = AsciiUtil.toUpperString(key._regn).intern();
+            String vart;
+            if (JDKIMPL) {
+                // preserve upper/lower cases
+                vart = key._vart.intern();
+            } else {
+                vart = AsciiUtil.toUpperString(key._vart).intern();
+            }
+            return new Key(lang, scrt, regn, vart);
+        }
+    }
+
+    private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
+
+        public Cache() {
+        }
+
+        protected Key normalizeKey(Key key) {
+            return Key.normalize(key);
+        }
+
+        protected BaseLocale createObject(Key key) {
+            return new BaseLocale(key._lang, key._scrt, key._regn, key._vart);
+        }
+
     }
 }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/Extension.java b/main/classes/core/src/com/ibm/icu/impl/locale/Extension.java
index 938e1e3..dec331c 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/Extension.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/Extension.java
@@ -1,12 +1,11 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 package com.ibm.icu.impl.locale;
 
-import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
 
 public class Extension {
     private char _key;
@@ -16,6 +15,11 @@
         _key = key;
     }
 
+    Extension(char key, String value) {
+        _key = key;
+        _value = value;
+    }
+
     public char getKey() {
         return _key;
     }
@@ -31,84 +35,4 @@
     public String toString() {
         return getID();
     }
-
-    public static Extension create(StringTokenIterator itr, ParseStatus sts) {
-        if (sts.isError() || itr.isDone()) {
-            return null;
-        }
-
-        Extension ext = null;
-        String key = itr.current();
-        if (LanguageTag.isExtensionSingleton(key) || LanguageTag.isPrivateuseSingleton(key)) {
-            itr.next();
-            ext = create(key.charAt(0), itr, sts);
-        }
-
-        return ext;
-    }
-
-    public static Extension create(char key, StringTokenIterator val, ParseStatus sts) {
-        if (sts.isError()) {
-            return null;
-        }
-        if (val.isDone()) {
-            sts.errorIndex = val.currentStart();
-            sts.errorMsg = "Missing extension subtag for extension :" + key;
-            return null;
-        }
-
-        Extension ext = null;
-        key = AsciiUtil.toLower(key);
-
-        switch (key) {
-        case UnicodeLocaleExtension.SINGLETON:
-            ext = new UnicodeLocaleExtension();
-            break;
-        case PrivateuseExtension.SINGLETON:
-            ext = new PrivateuseExtension();
-            break;
-        default:
-            ext = new Extension(key);
-            break;
-        }
-
-        ext.setExtensionValue(val, sts);
-
-        if (ext.getValue() == null) {
-            // return null only when nothing parsed.
-            return null;
-        }
-
-        return ext;
-    }
-
-    protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
-        if (sts.isError() || itr.isDone()) {
-            _value = null;
-            return;
-        }
-
-        StringBuilder buf = new StringBuilder();
-        while (!itr.isDone()) {
-            String s = itr.current();
-            if (!LanguageTag.isExtensionSubtag(s)) {
-                break;
-            }
-            s = LanguageTag.canonicalizeExtensionSubtag(s);
-            if (buf.length() != 0) {
-                buf.append(LanguageTag.SEP);
-            }
-            buf.append(s);
-            sts.parseLength = itr.currentEnd();
-            itr.next();
-        }
-
-        if (buf.length() == 0) {
-            sts.errorIndex = itr.currentStart();
-            sts.errorMsg = "Invalid extension subtag: " + itr.current(); 
-            _value = null;
-        } else {
-            _value = buf.toString();
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/InternalLocaleBuilder.java b/main/classes/core/src/com/ibm/icu/impl/locale/InternalLocaleBuilder.java
index f1d09ce..99c4fcb 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/InternalLocaleBuilder.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/InternalLocaleBuilder.java
@@ -1,170 +1,340 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 package com.ibm.icu.impl.locale;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
 
 public final class InternalLocaleBuilder {
 
+    private static final boolean JDKIMPL = false;
+
     private String _language = "";
     private String _script = "";
     private String _region = "";
     private String _variant = "";
-    private SortedMap<Character, Extension> _extMap;
 
-    private final boolean _lenientVariant;
+    private static final CaseInsensitiveChar PRIVUSE_KEY = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE.charAt(0));
 
-    private static final String LOCALESEP = "_";
+    private HashMap<CaseInsensitiveChar, String> _extensions;
+    private HashSet<CaseInsensitiveString> _uattributes;
+    private HashMap<CaseInsensitiveString, String> _ukeywords;
+
 
     public InternalLocaleBuilder() {
-        this(false);
-    }
-
-    public InternalLocaleBuilder(boolean lenientVariant) {
-        _lenientVariant = lenientVariant;
-    }
-
-    public boolean isLenientVariant() {
-        return _lenientVariant;
     }
 
     public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException {
-        String newval = "";
-        if (language.length() > 0) {
+        if (language == null || language.length() == 0) {
+            _language = "";
+        } else {
             if (!LanguageTag.isLanguage(language)) {
                 throw new LocaleSyntaxException("Ill-formed language: " + language, 0);
             }
-            newval = LanguageTag.canonicalizeLanguage(language);
+            _language = language;
         }
-        _language = newval;
         return this;
     }
 
     public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException {
-        String newval = "";
-        if (script.length() > 0) {
+        if (script == null || script.length() == 0) {
+            _script = "";
+        } else {
             if (!LanguageTag.isScript(script)) {
                 throw new LocaleSyntaxException("Ill-formed script: " + script, 0);
             }
-            newval = LanguageTag.canonicalizeScript(script);
+            _script = script;
         }
-        _script = newval;
         return this;
     }
 
     public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException {
-        String newval = "";
-        if (region.length() > 0) {
+        if (region == null || region.length() == 0) {
+            _region = "";
+        } else {
             if (!LanguageTag.isRegion(region)) {
-                throw new LocaleSyntaxException("Ill-formed region: " + region);
+                throw new LocaleSyntaxException("Ill-formed region: " + region, 0);
             }
-            newval = LanguageTag.canonicalizeRegion(region);
+            _region = region;
         }
-        _region = newval;
         return this;
     }
 
     public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException {
-        String newval = "";
-        if (variant.length() > 0) {
-            if (_lenientVariant) {
-                newval = variant;
+        if (variant == null || variant.length() == 0) {
+            _variant = "";
             } else {
-                newval = processVariant(variant);
+            // normalize separators to "_"
+            String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP);
+            int errIdx = checkVariants(var, BaseLocale.SEP);
+            if (errIdx != -1) {
+                throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
             }
+            _variant = var;
         }
-        _variant = newval;
+        return this;
+            }
+
+    public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
+        if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) {
+            throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
+        }
+        // Use case insensitive string to prevent duplication
+        if (_uattributes == null) {
+            _uattributes = new HashSet<CaseInsensitiveString>(4);
+        }
+        _uattributes.add(new CaseInsensitiveString(attribute));
         return this;
     }
 
-    public InternalLocaleBuilder setUnicodeLocaleExtension(String key, String type) throws LocaleSyntaxException {
-        if (key.length() == 0) {
-            throw new LocaleSyntaxException("Empty Unicode locale extension key");
+    public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException {
+        if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) {
+            throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute);
         }
+        if (_uattributes != null) {
+            _uattributes.remove(new CaseInsensitiveString(attribute));
+        }
+        return this;
+    }
+
+    public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException {
         if (!UnicodeLocaleExtension.isKey(key)) {
-            throw new LocaleSyntaxException("Ill-formed Unicode locale extension key: " + key, 0);
+            throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key);
         }
 
-        key = UnicodeLocaleExtension.canonicalizeKey(key);
-
-        UnicodeLocaleExtension ulext = null;
-        if (_extMap != null) {
-            ulext = (UnicodeLocaleExtension)_extMap.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
-        }
-
-        if (type.length() == 0) {
-            if (ulext != null) {
-                ulext.remove(key);
-                if (ulext.isEmpty()) {
-                    _extMap.remove(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
-                }
+        CaseInsensitiveString cikey = new CaseInsensitiveString(key);
+        if (type == null) {
+            if (_ukeywords != null) {
+                // null type is used for remove the key
+                _ukeywords.remove(cikey);
             }
         } else {
-            StringBuilder buf = new StringBuilder();
-            StringTokenIterator sti = new StringTokenIterator(type, LanguageTag.SEP);
-            for (String subtag = sti.first(); !sti.isDone(); subtag = sti.next()) {
-                if (!UnicodeLocaleExtension.isTypeSubtag(subtag)) {
-                    throw new LocaleSyntaxException("Ill-formed Unicode locale extension type: " + type, sti.currentStart());
+            if (type.length() != 0) {
+                // normalize separator to "-"
+                String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
+                // validate
+                StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP);
+                while (!itr.isDone()) {
+                    String s = itr.current();
+                    if (!UnicodeLocaleExtension.isTypeSubtag(s)) {
+                        throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: " + type, itr.currentStart());
+                    }
+                    itr.next();
                 }
-                if (buf.length() > 0) {
-                    buf.append(LanguageTag.SEP);
-                }
-                buf.append(UnicodeLocaleExtension.canonicalizeTypeSubtag(subtag));
             }
-            if (ulext == null) {
-                SortedMap<String, String> ktmap = new TreeMap<String, String>();
-                ktmap.put(key, buf.toString());
-                ulext = new UnicodeLocaleExtension(ktmap);
-                if (_extMap == null) {
-                    _extMap = new TreeMap<Character, Extension>();
-                }
-                _extMap.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), ulext);
-            } else {
-                ulext.put(key, buf.toString());
+            if (_ukeywords == null) {
+                _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
             }
+            _ukeywords.put(cikey, type);
         }
-
         return this;
     }
 
     public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException {
-        String strSingleton = String.valueOf(singleton);
-        if (!LanguageTag.isExtensionSingleton(strSingleton) && !LanguageTag.isPrivateuseSingleton(strSingleton)) {
+        // validate key
+        boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton);
+        if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) {
             throw new LocaleSyntaxException("Ill-formed extension key: " + singleton);
         }
 
-        strSingleton = LanguageTag.canonicalizeExtensionSingleton(strSingleton);
-        Character key = Character.valueOf(strSingleton.charAt(0));
+        boolean remove = (value == null || value.length() == 0);
+        CaseInsensitiveChar key = new CaseInsensitiveChar(singleton);
 
-        if (value.length() == 0) {
-            if (_extMap != null) {
-                _extMap.remove(key);
+        if (remove) {
+            if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
+                // clear entire Unicode locale extension
+                if (_uattributes != null) {
+                    _uattributes.clear();
+                }
+                if (_ukeywords != null) {
+                    _ukeywords.clear();
             }
         } else {
-            StringTokenIterator sti = new StringTokenIterator(value, LanguageTag.SEP);
-            ParseStatus sts = new ParseStatus();
+                if (_extensions != null && _extensions.containsKey(key)) {
+                    _extensions.remove(key);
+                }
+                }
+        } else {
+            // validate value
+            String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
+            StringTokenIterator itr = new StringTokenIterator(val, LanguageTag.SEP);
+            while (!itr.isDone()) {
+                String s = itr.current();
+                boolean validSubtag;
+                if (isBcpPrivateuse) {
+                    validSubtag = LanguageTag.isPrivateuseSubtag(s);
+                } else {
+                    validSubtag = LanguageTag.isExtensionSubtag(s);
+                }
+                if (!validSubtag) {
+                    throw new LocaleSyntaxException("Ill-formed extension value: " + s, itr.currentStart());
+            }
+                itr.next();
+                }
 
-            Extension ext = Extension.create(key.charValue(), sti, sts);
-            if (sts.isError()) {
-                throw new LocaleSyntaxException(sts.errorMsg, sts.errorIndex);
+            if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
+                setUnicodeLocaleExtension(val);
+            } else {
+                if (_extensions == null) {
+                    _extensions = new HashMap<CaseInsensitiveChar, String>(4);
+                }
+                _extensions.put(key, val);
             }
-            if (sts.parseLength != value.length() || ext == null) {
-                throw new LocaleSyntaxException("Ill-formed extension value: " + value, sti.currentStart());
             }
-            if (_extMap == null) {
-                _extMap = new TreeMap<Character, Extension>();
-            }
-            _extMap.put(key, ext);
+        return this;
         }
+
+    /*
+     * Set extension/private subtags in a single string representation
+     */
+    public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException {
+        if (subtags == null || subtags.length() == 0) {
+            clearExtensions();
+        return this;
+    }
+        subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP);
+        StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
+
+        List<String> extensions = null;
+        String privateuse = null;
+
+        int parsed = 0;
+        int start;
+
+        // Make a list of extension subtags
+        while (!itr.isDone()) {
+            String s = itr.current();
+            if (LanguageTag.isExtensionSingleton(s)) {
+                start = itr.currentStart();
+                String singleton = s;
+                StringBuilder sb = new StringBuilder(singleton);
+
+                itr.next();
+                while (!itr.isDone()) {
+                    s = itr.current();
+                    if (LanguageTag.isExtensionSubtag(s)) {
+                        sb.append(LanguageTag.SEP).append(s);
+                        parsed = itr.currentEnd();
+                    } else {
+                        break;
+                    }
+                    itr.next();
+                }
+
+                if (parsed < start) {
+                    throw new LocaleSyntaxException("Incomplete extension '" + singleton + "'", start);
+                }
+
+                if (extensions == null) {
+                    extensions = new ArrayList<String>(4);
+                }
+                extensions.add(sb.toString());
+            } else {
+                break;
+            }
+        }
+        if (!itr.isDone()) {
+            String s = itr.current();
+            if (LanguageTag.isPrivateusePrefix(s)) {
+                start = itr.currentStart();
+                StringBuilder sb = new StringBuilder(s);
+
+                itr.next();
+                while (!itr.isDone()) {
+                    s = itr.current();
+                    if (!LanguageTag.isPrivateuseSubtag(s)) {
+                        break;
+                    }
+                    sb.append(LanguageTag.SEP).append(s);
+                    parsed = itr.currentEnd();
+
+                    itr.next();
+            }
+                if (parsed <= start) {
+                    throw new LocaleSyntaxException("Incomplete privateuse:" + subtags.substring(start), start);
+        } else {
+                    privateuse = sb.toString();
+                }
+            }
+        }
+
+        if (!itr.isDone()) {
+            throw new LocaleSyntaxException("Ill-formed extension subtags:" + subtags.substring(itr.currentStart()), itr.currentStart());
+        }
+
+        return setExtensions(extensions, privateuse);
+    }
+
+    /*
+     * Set a list of BCP47 extensions and private use subtags
+     * BCP47 extensions are already validated and well-formed, but may contain duplicates
+     */
+    private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) {
+        clearExtensions();
+
+        if (bcpExtensions != null && bcpExtensions.size() > 0) {
+            HashSet<CaseInsensitiveChar> processedExtensions = new HashSet<CaseInsensitiveChar>(bcpExtensions.size());
+            for (String bcpExt : bcpExtensions) {
+                CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt.charAt(0));
+                // ignore duplicates
+                if (!processedExtensions.contains(key)) {
+                    // each extension string contains singleton, e.g. "a-abc-def"
+                    if (UnicodeLocaleExtension.isSingletonChar(key.value())) {
+                        setUnicodeLocaleExtension(bcpExt.substring(2));
+                    } else {
+                        if (_extensions == null) {
+                            _extensions = new HashMap<CaseInsensitiveChar, String>(4);
+                        }
+                        _extensions.put(key, bcpExt.substring(2));
+                    }
+                }
+            }
+            }
+        if (privateuse != null && privateuse.length() > 0) {
+            // privateuse string contains prefix, e.g. "x-abc-def"
+            if (_extensions == null) {
+                _extensions = new HashMap<CaseInsensitiveChar, String>(1);
+            }
+            _extensions.put(new CaseInsensitiveChar(privateuse.charAt(0)), privateuse.substring(2));
+        }
+
+        return this;
+    }
+
+    /*
+     * Reset Builder's internal state with the given language tag
+     */
+    public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) {
+        clear();
+        if (langtag.getExtlangs().size() > 0) {
+            _language = langtag.getExtlangs().get(0);
+        } else {
+            String language = langtag.getLanguage();
+            if (!language.equals(LanguageTag.UNDETERMINED)) {
+                _language = language;
+            }
+        }
+        _script = langtag.getScript();
+        _region = langtag.getRegion();
+
+        List<String> bcpVariants = langtag.getVariants();
+        if (bcpVariants.size() > 0) {
+            StringBuilder var = new StringBuilder(bcpVariants.get(0));
+            for (int i = 1; i < bcpVariants.size(); i++) {
+                var.append(BaseLocale.SEP).append(bcpVariants.get(i));
+            }
+            _variant = var.toString();
+        }
+
+        setExtensions(langtag.getExtensions(), langtag.getPrivateuse());
+
         return this;
     }
 
@@ -174,65 +344,89 @@
         String region = base.getRegion();
         String variant = base.getVariant();
 
-        // Validate base locale fields before updating internal state.
-        // LocaleExtensions always store validated/canonicalized values,
-        // so no checks are necessary.
-        if (language.length() > 0) {
-            if (!LanguageTag.isLanguage(language)) {
-                throw new LocaleSyntaxException("Ill-formed language: " + language);
+        if (JDKIMPL) {
+            // Special backward compatibility support
+
+            // Exception 1 - ja_JP_JP
+            if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) {
+                // When locale ja_JP_JP is created, ca-japanese is always there.
+                // The builder ignores the variant "JP"
+                assert("japanese".equals(extensions.getUnicodeLocaleType("ca")));
+                variant = "";
             }
-            language = LanguageTag.canonicalizeLanguage(language);
-        }
-        if (script.length() > 0) {
-            if (!LanguageTag.isScript(script)) {
-                throw new LocaleSyntaxException("Ill-formed script: " + script);
+            // Exception 2 - th_TH_TH
+            else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) {
+                // When locale th_TH_TH is created, nu-thai is always there.
+                // The builder ignores the variant "TH"
+                assert("thai".equals(extensions.getUnicodeLocaleType("nu")));
+                variant = "";
             }
-            script = LanguageTag.canonicalizeScript(script);
-        }
-        if (region.length() > 0) {
-            if (!LanguageTag.isRegion(region)) {
-                throw new LocaleSyntaxException("Ill-formed region: " + region);
-            }
-            region = LanguageTag.canonicalizeRegion(region);
-        }
-        if (_lenientVariant) {
-            // In lenient variant mode, parse special private use value
-            // reserved for Java Locale.
-            String privuse = extensions.getExtensionValue(Character.valueOf(LanguageTag.PRIVATEUSE.charAt(0)));
-            if (privuse != null) {
-                variant = LanguageTag.getJavaCompatibleVariant(variant, privuse);
-            }
-        } else {
-            if (variant.length() > 0) {
-                variant = processVariant(variant);
+            // Exception 3 - no_NO_NY
+            else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) {
+                // no_NO_NY is a valid locale and used by Java 6 or older versions.
+                // The build ignores the variant "NY" and change the language to "nn".
+                language = "nn";
+                variant = "";
             }
         }
 
-        // update builder's internal fields
+        // Validate base locale fields before updating internal state.
+        // LocaleExtensions always store validated/canonicalized values,
+        // so no checks are necessary.
+        if (language.length() > 0 && !LanguageTag.isLanguage(language)) {
+                throw new LocaleSyntaxException("Ill-formed language: " + language);
+            }
+
+        if (script.length() > 0 && !LanguageTag.isScript(script)) {
+                throw new LocaleSyntaxException("Ill-formed script: " + script);
+            }
+
+        if (region.length() > 0 && !LanguageTag.isRegion(region)) {
+                throw new LocaleSyntaxException("Ill-formed region: " + region);
+            }
+
+            if (variant.length() > 0) {
+            int errIdx = checkVariants(variant, BaseLocale.SEP);
+            if (errIdx != -1) {
+                throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx);
+            }
+        }
+
+        // The input locale is validated at this point.
+        // Now, updating builder's internal fields.
         _language = language;
         _script = script;
         _region = region;
         _variant = variant;
+        clearExtensions();
 
-        // empty extensions
-        if (_extMap == null) {
-            _extMap = new TreeMap<Character, Extension>();
+        Set<Character> extKeys = (extensions == null) ? null : extensions.getKeys();
+        if (extKeys != null) {
+            // map extensions back to builder's internal format
+            for (Character key : extKeys) {
+                Extension e = extensions.getExtension(key);
+                if (e instanceof UnicodeLocaleExtension) {
+                    UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e;
+                    for (String uatr : ue.getUnicodeLocaleAttributes()) {
+                        if (_uattributes == null) {
+                            _uattributes = new HashSet<CaseInsensitiveString>(4);
+                        }
+                        _uattributes.add(new CaseInsensitiveString(uatr));
+                    }
+                    for (String ukey : ue.getUnicodeLocaleKeys()) {
+                        if (_ukeywords == null) {
+                            _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
+                        }
+                        _ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey));
+                    }
         } else {
-            _extMap.clear();
+                    if (_extensions == null) {
+                        _extensions = new HashMap<CaseInsensitiveChar, String>(4);
         }
-
-        Set<Character> extKeys = extensions.getKeys();
-        for (Character key : extKeys) {
-            Extension ext = extensions.getExtension(key);
-            if (_lenientVariant && (ext instanceof PrivateuseExtension)) {
-                String modPrivuse = LanguageTag.getJavaCompatiblePrivateuse(ext.getValue());
-                if (!modPrivuse.equals(ext.getValue())) {
-                    ext = new PrivateuseExtension(modPrivuse);
+                    _extensions.put(new CaseInsensitiveChar(key.charValue()), e.getValue());
                 }
             }
-            _extMap.put(key, ext);
         }
-
         return this;
     }
 
@@ -241,44 +435,250 @@
         _script = "";
         _region = "";
         _variant = "";
-        removeLocaleExtensions();
+        clearExtensions();
         return this;
     }
 
-    public InternalLocaleBuilder removeLocaleExtensions() {
-        if (_extMap != null) {
-            _extMap.clear();
+    public InternalLocaleBuilder clearExtensions() {
+        if (_extensions != null) {
+            _extensions.clear();
+        }
+        if (_uattributes != null) {
+            _uattributes.clear();
+        }
+        if (_ukeywords != null) {
+            _ukeywords.clear();
         }
         return this;
     }
 
     public BaseLocale getBaseLocale() {
-        return BaseLocale.getInstance(_language, _script, _region, _variant);
+        String language = _language;
+        String script = _script;
+        String region = _region;
+        String variant = _variant;
+
+        // Special private use subtag sequence identified by "lvariant" will be
+        // interpreted as Java variant.
+        if (_extensions != null) {
+            String privuse = _extensions.get(PRIVUSE_KEY);
+            if (privuse != null) {
+                StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP);
+                boolean sawPrefix = false;
+                int privVarStart = -1;
+                while (!itr.isDone()) {
+                    if (sawPrefix) {
+                        privVarStart = itr.currentStart();
+                        break;
+                    }
+                    if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
+                        sawPrefix = true;
+                    }
+                    itr.next();
+                }
+                if (privVarStart != -1) {
+                    StringBuilder sb = new StringBuilder(variant);
+                    if (sb.length() != 0) {
+                        sb.append(BaseLocale.SEP);
+                    }
+                    sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP, BaseLocale.SEP));
+                    variant = sb.toString();
+                }
+            }
+        }
+
+        return BaseLocale.getInstance(language, script, region, variant);
     }
 
     public LocaleExtensions getLocaleExtensions() {
-        if (_extMap != null && _extMap.size() > 0) {
-            return LocaleExtensions.getInstance(_extMap);
+        if ((_extensions == null || _extensions.size() == 0)
+                && (_uattributes == null || _uattributes.size() == 0)
+                && (_ukeywords == null || _ukeywords.size() == 0)) {
+            return LocaleExtensions.EMPTY_EXTENSIONS;
         }
-        return LocaleExtensions.EMPTY_EXTENSIONS;
+
+        return new LocaleExtensions(_extensions, _uattributes, _ukeywords);
+        }
+
+    /*
+     * Remove special private use subtag sequence identified by "lvariant"
+     * and return the rest. Only used by LocaleExtensions
+     */
+    static String removePrivateuseVariant(String privuseVal) {
+        StringTokenIterator itr = new StringTokenIterator(privuseVal, LanguageTag.SEP);
+
+        // Note: privateuse value "abc-lvariant" is unchanged
+        // because no subtags after "lvariant".
+
+        int prefixStart = -1;
+        boolean sawPrivuseVar = false;
+        while (!itr.isDone()) {
+            if (prefixStart != -1) {
+                // Note: privateuse value "abc-lvariant" is unchanged
+                // because no subtags after "lvariant".
+                sawPrivuseVar = true;
+                break;
+            }
+            if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) {
+                prefixStart = itr.currentStart();
+            }
+            itr.next();
+        }
+        if (!sawPrivuseVar) {
+            return privuseVal;
+        }
+
+        assert(prefixStart == 0 || prefixStart > 1);
+        return (prefixStart == 0) ? null : privuseVal.substring(0, prefixStart -1);
     }
 
-    private String processVariant(String variant) throws LocaleSyntaxException {
-        StringTokenIterator sti = new StringTokenIterator(variant, LOCALESEP);
-        ParseStatus sts = new ParseStatus();
-
-        List<String> variants = LanguageTag.DEFAULT_PARSER.parseVariants(sti, sts);
-        if (sts.parseLength != variant.length()) {
-            throw new LocaleSyntaxException("Ill-formed variant: " + variant, sti.currentStart());
-        }
-
-        StringBuilder buf = new StringBuilder();
-        for (String var : variants) {
-            if (buf.length() != 0) {
-                buf.append(LOCALESEP);
+    /*
+     * Check if the given variant subtags separated by the given
+     * separator(s) are valid
+     */
+    private int checkVariants(String variants, String sep) {
+        StringTokenIterator itr = new StringTokenIterator(variants, sep);
+        while (!itr.isDone()) {
+            String s = itr.current();
+            if (!LanguageTag.isVariant(s)) {
+                return itr.currentStart();
             }
-            buf.append(var);
+            itr.next();
         }
-        return buf.toString();
+        return -1;
+    }
+
+    /*
+     * Private methods parsing Unicode Locale Extension subtags.
+     * Duplicated attributes/keywords will be ignored.
+     * The input must be a valid extension subtags (excluding singleton).
+     */
+    private void setUnicodeLocaleExtension(String subtags) {
+        // wipe out existing attributes/keywords
+        if (_uattributes != null) {
+            _uattributes.clear();
+        }
+        if (_ukeywords != null) {
+            _ukeywords.clear();
+        }
+
+        StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP);
+
+        // parse attributes
+        while (!itr.isDone()) {
+            if (!UnicodeLocaleExtension.isAttribute(itr.current())) {
+                break;
+            }
+            if (_uattributes == null) {
+                _uattributes = new HashSet<CaseInsensitiveString>(4);
+            }
+            _uattributes.add(new CaseInsensitiveString(itr.current()));
+            itr.next();
+        }
+
+        // parse keywords
+        CaseInsensitiveString key = null;
+        String type;
+        int typeStart = -1;
+        int typeEnd = -1;
+        while (!itr.isDone()) {
+            if (key != null) {
+                if (UnicodeLocaleExtension.isKey(itr.current())) {
+                    // next keyword - emit previous one
+                    assert(typeStart == -1 || typeEnd != -1);
+                    type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
+                    if (_ukeywords == null) {
+                        _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
+                    }
+                    _ukeywords.put(key, type);
+
+                    // reset keyword info
+                    CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current());
+                    key = _ukeywords.containsKey(tmpKey) ? null : tmpKey;
+                    typeStart = typeEnd = -1;
+                } else {
+                    if (typeStart == -1) {
+                        typeStart = itr.currentStart();
+                    }
+                    typeEnd = itr.currentEnd();
+                }
+            } else if (UnicodeLocaleExtension.isKey(itr.current())) {
+                // 1. first keyword or
+                // 2. next keyword, but previous one was duplicate
+                key = new CaseInsensitiveString(itr.current());
+                if (_ukeywords != null && _ukeywords.containsKey(key)) {
+                    // duplicate
+                    key = null;
+                }
+            }
+
+            if (!itr.hasNext()) {
+                if (key != null) {
+                    // last keyword
+                    assert(typeStart == -1 || typeEnd != -1);
+                    type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd);
+                    if (_ukeywords == null) {
+                        _ukeywords = new HashMap<CaseInsensitiveString, String>(4);
+                    }
+                    _ukeywords.put(key, type);
+                }
+                break;
+            }
+
+            itr.next();
+        }
+    }
+
+    static class CaseInsensitiveString {
+        private String _s;
+
+        CaseInsensitiveString(String s) {
+            _s = s;
+        }
+
+        public String value() {
+            return _s;
+        }
+
+        public int hashCode() {
+            return AsciiUtil.toLowerString(_s).hashCode();
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof CaseInsensitiveString)) {
+                return false;
+            }
+            return AsciiUtil.caseIgnoreMatch(_s, ((CaseInsensitiveString)obj).value());
+        }
+    }
+
+    static class CaseInsensitiveChar {
+        private char _c;
+
+        CaseInsensitiveChar(char c) {
+            _c = c;
+    }
+
+        public char value() {
+            return _c;
+        }
+
+        public int hashCode() {
+            return AsciiUtil.toLower(_c);
+        }
+
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof CaseInsensitiveChar)) {
+                return false;
+        }
+            return _c ==  AsciiUtil.toLower(((CaseInsensitiveChar)obj).value());
+        }
+
     }
 }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/LanguageTag.java b/main/classes/core/src/com/ibm/icu/impl/locale/LanguageTag.java
index 39e549b..a6f4c64 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/LanguageTag.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/LanguageTag.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2010-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -12,12 +12,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.Map.Entry;
 
 public class LanguageTag {
-
     private static final boolean JDKIMPL = false;
 
     //
@@ -26,31 +22,19 @@
     public static final String SEP = "-";
     public static final String PRIVATEUSE = "x";
     public static String UNDETERMINED = "und";
-
-    private static final String JAVAVARIANT = "variant";
-    private static final String JAVASEP = "_";
-
-    private static final SortedMap<Character, Extension> EMPTY_EXTENSION_MAP = new TreeMap<Character, Extension>();
-
-    //
-    // Language tag parser instances
-    //
-    public static final Parser DEFAULT_PARSER = new Parser(false);
-    public static final Parser JAVA_VARIANT_COMPATIBLE_PARSER = new Parser(true);
+    public static final String PRIVUSE_VARIANT_PREFIX = "lvariant";
 
     //
     // Language subtag fields
     //
-    private String _grandfathered = ""; // grandfathered tag
     private String _language = "";      // language subtag
     private String _script = "";        // script subtag
     private String _region = "";        // region subtag
-    private String _privateuse = "";    // privateuse, not including leading "x-"
+    private String _privateuse = "";    // privateuse
+
     private List<String> _extlangs = Collections.emptyList();   // extlang subtags
     private List<String> _variants = Collections.emptyList();   // variant subtags
-    private SortedMap<Character, Extension> _extensions = EMPTY_EXTENSION_MAP;  // extension key/value pairs
-
-    private boolean _javaCompatVariants = false;
+    private List<String> _extensions = Collections.emptyList(); // extensions
 
     // Map contains grandfathered tags and its preferred mappings from
     // http://www.ietf.org/rfc/rfc5646.txt
@@ -92,16 +76,16 @@
         final String[][] entries = {
           //{"tag",         "preferred"},
             {"art-lojban",  "jbo"},
-            {"cel-gaulish", "cel-gaulish"}, // gaulish is parsed as a variant
-            {"en-GB-oed",   "en-GB"},       // oed (Oxford English Dictionary spelling) is ignored
+            {"cel-gaulish", "xtg-x-cel-gaulish"},   // fallback
+            {"en-GB-oed",   "en-GB-x-oed"},         // fallback
             {"i-ami",       "ami"},
             {"i-bnn",       "bnn"},
-            {"i-default",   UNDETERMINED},  // fallback
-            {"i-enochian",  UNDETERMINED},  // fallback
+            {"i-default",   "en-x-i-default"},      // fallback
+            {"i-enochian",  "und-x-i-enochian"},    // fallback
             {"i-hak",       "hak"},
             {"i-klingon",   "tlh"},
             {"i-lux",       "lb"},
-            {"i-mingo",     UNDETERMINED},  // fallback
+            {"i-mingo",     "see-x-i-mingo"},       // fallback
             {"i-navajo",    "nv"},
             {"i-pwn",       "pwn"},
             {"i-tao",       "tao"},
@@ -114,7 +98,7 @@
             {"sgn-CH-DE",   "sgg"},
             {"zh-guoyu",    "cmn"},
             {"zh-hakka",    "hak"},
-            {"zh-min",      "zh"},          // fallback
+            {"zh-min",      "nan-x-zh-min"},        // fallback
             {"zh-min-nan",  "nan"},
             {"zh-xiang",    "hsn"},
         };
@@ -126,459 +110,7 @@
     private LanguageTag() {
     }
 
-    //
-    // Getter methods for language subtag fields
-    //
-
-    public String getLanguage() {
-        return _language;
-    }
-
-    public List<String> getExtlangs() {
-        return Collections.unmodifiableList(_extlangs);
-    }
-
-    public String getScript() {
-        return _script;
-    }
-
-    public String getRegion() {
-        return _region;
-    }
-
-    public List<String> getVariants() {
-        return Collections.unmodifiableList(_variants);
-    }
-
-    public SortedMap<Character, Extension> getExtensions() {
-        return Collections.unmodifiableSortedMap(_extensions);
-    }
-
-    public String getPrivateuse() {
-        return _privateuse;
-    }
-
-    public String getGrandfathered() {
-        return _grandfathered;
-    }
-
-    private String getJavaVariant() {
-        StringBuilder buf = new StringBuilder();
-        for (String var : _variants) {
-            if (buf.length() > 0) {
-                buf.append(JAVASEP);
-            }
-            buf.append(var);
-        }
-        if (_javaCompatVariants) {
-            return getJavaCompatibleVariant(buf.toString(), _privateuse);
-        }
-
-        return buf.toString();
-    }
-
-    private String getJavaPrivateuse() {
-        if (_javaCompatVariants) {
-            return getJavaCompatiblePrivateuse(_privateuse);
-        }
-        return _privateuse;
-    }
-
-    static String getJavaCompatibleVariant(String bcpVariants, String bcpPrivuse) {
-        StringBuilder buf = new StringBuilder(bcpVariants);
-        if (bcpPrivuse.length() > 0) {
-            int idx = -1;
-            if (bcpPrivuse.startsWith(JAVAVARIANT + SEP)) {
-                idx = (JAVAVARIANT + SEP).length();
-            } else {
-                idx = bcpPrivuse.indexOf(SEP + JAVAVARIANT + SEP);
-                if (idx != -1) {
-                    idx += (SEP + JAVAVARIANT + SEP).length();
-                }
-            }
-            if (idx != -1) {
-                if (buf.length() != 0) {
-                    buf.append(JAVASEP);
-                }
-                buf.append(bcpPrivuse.substring(idx).replace(SEP, JAVASEP));
-            }
-        }
-        return buf.toString();
-    }
-
-    static String getJavaCompatiblePrivateuse(String bcpPrivuse) {
-        if (bcpPrivuse.length() > 0) {
-            int idx = -1;
-            if (bcpPrivuse.startsWith(JAVAVARIANT + SEP)) {
-                idx = 0;
-            } else {
-                idx = bcpPrivuse.indexOf(SEP + JAVAVARIANT + SEP);
-            }
-            if (idx != -1) {
-                return bcpPrivuse.substring(0, idx);
-            }
-        }
-        return bcpPrivuse;
-    }
-
-    public BaseLocale getBaseLocale() {
-        String lang = _language;
-        if (_extlangs.size() > 0) {
-            // Extended language subtags are used for various historical
-            // and compatibility reasons.  Each extended language subtag
-            // has a "Preferred-Value', that is exactly same with the extended
-            // language subtag itself.  For example,
-            //
-            // Type: extlang
-            // Subtag: aao
-            // Description: Algerian Saharan Arabic
-            // Added: 2009-07-29
-            // Preferred-Value: aao
-            // Prefix: ar
-            // Macrolanguage: ar
-            //
-            // For example, language tag "ar-aao-DZ" is equivalent to
-            // "aao-DZ".
-            //
-            // Strictly speaking, the mapping requires prefix validation 
-            // (e.g. primary language must be "ar" in the example above).
-            // However, this implementation does not check the prefix
-            // and simply use the first extlang value as locale's language.
-            lang = _extlangs.get(0);
-        }
-        if (lang.equals(UNDETERMINED)) {
-            lang = "";
-        }
-        return BaseLocale.getInstance(lang, _script, _region, getJavaVariant());
-    }
-
-    public LocaleExtensions getLocaleExtensions() {
-        String javaPrivuse = getJavaPrivateuse();
-        if (_extensions == null && javaPrivuse.length() == 0) {
-            return LocaleExtensions.EMPTY_EXTENSIONS;
-        }
-        SortedMap<Character, Extension> exts = new TreeMap<Character, Extension>();
-        if (_extensions != null) {
-            exts.putAll(_extensions);
-        }
-        if (javaPrivuse.length() > 0) {
-            PrivateuseExtension pext = new PrivateuseExtension(javaPrivuse);
-            exts.put(Character.valueOf(PrivateuseExtension.SINGLETON), pext);
-        }
-        return LocaleExtensions.getInstance(exts);
-    }
-
-    public String getID() {
-        if (_grandfathered.length() > 0) {
-            return _grandfathered;
-        }
-        StringBuilder buf = new StringBuilder();
-        if (_language.length() > 0) {
-            buf.append(_language);
-            if (_extlangs.size() > 0) {
-                for (String el : _extlangs) {
-                    buf.append(SEP);
-                    buf.append(el);
-                }
-            }
-            if (_script.length() > 0) {
-                buf.append(SEP);
-                buf.append(_script);
-            }
-            if (_region.length() > 0) {
-                buf.append(SEP);
-                buf.append(_region);
-            }
-            if (_variants.size() > 0) {
-                for (String var : _variants) {
-                    buf.append(SEP);
-                    buf.append(var);
-                }
-            }
-            if (_extensions.size() > 0) {
-                Set<Entry<Character, Extension>> exts = _extensions.entrySet();
-                for (Entry<Character, Extension> ext : exts) {
-                    buf.append(SEP);
-                    buf.append(ext.getKey());
-                    buf.append(SEP);
-                    buf.append(ext.getValue().getValue());
-                }
-            }
-        }
-        if (_privateuse.length() > 0) {
-            if (buf.length() > 0) {
-                buf.append(SEP);
-            }
-            buf.append(PRIVATEUSE);
-            buf.append(SEP);
-            buf.append(_privateuse);
-        }
-        return buf.toString();
-    }
-
-    public String toString() {
-        return getID();
-    }
-
-    //
-    // Language subtag syntax checking methods
-    //
-
-    public static boolean isLanguage(String s) {
-        // language      = 2*3ALPHA            ; shortest ISO 639 code
-        //                 ["-" extlang]       ; sometimes followed by
-        //                                     ;   extended language subtags
-        //               / 4ALPHA              ; or reserved for future use
-        //               / 5*8ALPHA            ; or registered language subtag
-        return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaString(s);
-    }
-
-    public static boolean isExtlang(String s) {
-        // extlang       = 3ALPHA              ; selected ISO 639 codes
-        //                 *2("-" 3ALPHA)      ; permanently reserved
-        return (s.length() == 3) && AsciiUtil.isAlphaString(s);
-    }
-
-    public static boolean isScript(String s) {
-        // script        = 4ALPHA              ; ISO 15924 code
-        return (s.length() == 4) && AsciiUtil.isAlphaString(s);
-    }
-
-    public static boolean isRegion(String s) {
-        // region        = 2ALPHA              ; ISO 3166-1 code
-        //               / 3DIGIT              ; UN M.49 code
-        return ((s.length() == 2) && AsciiUtil.isAlphaString(s))
-                || ((s.length() == 3) && AsciiUtil.isNumericString(s));
-    }
-
-    public static boolean isVariant(String s) {
-        // variant       = 5*8alphanum         ; registered variants
-        //               / (DIGIT 3alphanum)
-        int len = s.length();
-        if (len >= 5 && len <= 8) {
-            return AsciiUtil.isAlphaNumericString(s);
-        }
-        if (len == 4) {
-            return AsciiUtil.isNumeric(s.charAt(0))
-                    && AsciiUtil.isAlphaNumeric(s.charAt(1))
-                    && AsciiUtil.isAlphaNumeric(s.charAt(2))
-                    && AsciiUtil.isAlphaNumeric(s.charAt(3));
-        }
-        return false;
-    }
-
-    public static boolean isExtensionSingleton(String s) {
-        // singleton     = DIGIT               ; 0 - 9
-        //               / %x41-57             ; A - W
-        //               / %x59-5A             ; Y - Z
-        //               / %x61-77             ; a - w
-        //               / %x79-7A             ; y - z
-
-        return (s.length() == 1)
-                && AsciiUtil.isAlphaString(s)
-                && !AsciiUtil.caseIgnoreMatch(PRIVATEUSE, s);
-    }
-
-    public static boolean isExtensionSubtag(String s) {
-        // extension     = singleton 1*("-" (2*8alphanum))
-        return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
-    }
-
-    public static boolean isPrivateuseSingleton(String s) {
-        // privateuse    = "x" 1*("-" (1*8alphanum))
-        return (s.length() == 1)
-                && AsciiUtil.caseIgnoreMatch(PRIVATEUSE, s);
-    }
-
-    public static boolean isPrivateuseSubtag(String s) {
-        // privateuse    = "x" 1*("-" (1*8alphanum))
-        return (s.length() >= 1) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
-    }
-
-    //
-    // Language subtag canonicalization methods
-    //
-
-    public static String canonicalizeLanguage(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-    public static String canonicalizeExtlang(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-    public static String canonicalizeScript(String s) {
-        return AsciiUtil.toTitleString(s);
-    }
-
-    public static String canonicalizeRegion(String s) {
-        return AsciiUtil.toUpperString(s);
-    }
-
-    public static String canonicalizeVariant(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-    public static String canonicalizeExtensionSingleton(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-    public static String canonicalizeExtensionSubtag(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-    public static String canonicalizePrivateuseSubtag(String s) {
-        return AsciiUtil.toLowerString(s);
-    }
-
-
-    public static LanguageTag parse(String str, boolean javaCompatVar) {
-        LanguageTag tag = new LanguageTag();
-        tag.parseString(str, javaCompatVar);
-        return tag;
-    }
-
-    public static LanguageTag parseStrict(String str, boolean javaCompatVar) throws LocaleSyntaxException {
-        LanguageTag tag = new LanguageTag();
-        ParseStatus sts = tag.parseString(str, javaCompatVar);
-        if (sts.isError()) {
-            throw new LocaleSyntaxException(sts.errorMsg, sts.errorIndex);
-        }
-        return tag;
-    }
-
-    public static LanguageTag parseLocale(BaseLocale base, LocaleExtensions locExts) {
-        LanguageTag tag = new LanguageTag();
-        tag._javaCompatVariants = true;
-
-        String language = base.getLanguage();
-        String script = base.getScript();
-        String region = base.getRegion();
-        String variant = base.getVariant();
-
-        String privuseVar = null;   // store ill-formed variant subtags
-
-        if (language.length() > 0 && isLanguage(language)) {
-            // Convert a deprecated language code used by Java to
-            // a new code
-            language = canonicalizeLanguage(language);
-            if (language.equals("iw")) {
-                language = "he";
-            } else if (language.equals("ji")) {
-                language = "yi";
-            } else if (language.equals("in")) {
-                language = "id";
-            }
-            tag._language = language;
-        }
-        if (script.length() > 0 && isScript(script)) {
-            tag._script = canonicalizeScript(script);
-        }
-        if (region.length() > 0 && isRegion(region)) {
-            tag._region = canonicalizeRegion(region);
-        }
-        if (variant.length() > 0) {
-            List<String> variants = null;
-            StringTokenIterator varitr = new StringTokenIterator(variant, JAVASEP);
-            while (!varitr.isDone()) {
-                String var = varitr.current();
-                if (!isVariant(var)) {
-                    break;
-                }
-                if (variants == null) {
-                    variants = new ArrayList<String>();
-                }
-                if (JDKIMPL) {
-                    variants.add(var);  // Do not canonicalize!
-                } else {
-                    variants.add(canonicalizeVariant(var));
-                }
-                varitr.next();
-            }
-            if (variants != null) {
-                tag._variants = variants;
-            }
-            if (!varitr.isDone()) {
-                // ill-formed variant subtags
-                StringBuilder buf = new StringBuilder();
-                while (!varitr.isDone()) {
-                    String prvv = varitr.current();
-                    if (!isPrivateuseSubtag(prvv)) {
-                        // cannot use private use subtag - truncated
-                        break;
-                    }
-                    if (buf.length() > 0) {
-                        buf.append(SEP);
-                    }
-                    if (!JDKIMPL) {
-                        prvv = AsciiUtil.toLowerString(prvv);
-                    }
-                    buf.append(prvv);
-                    varitr.next();
-                }
-                if (buf.length() > 0) {
-                    privuseVar = buf.toString();
-                }
-            }
-        }
-
-        TreeMap<Character, Extension> extensions = null;
-        String privateuse = null;
-
-        Set<Character> locextKeys = locExts.getKeys();
-        for (Character locextKey : locextKeys) {
-            Extension ext = locExts.getExtension(locextKey);
-            if (ext instanceof PrivateuseExtension) {
-                privateuse = ext.getValue();
-            } else {
-                if (extensions == null) {
-                    extensions = new TreeMap<Character, Extension>();
-                }
-                extensions.put(locextKey, ext);
-            }
-        }
-
-        if (extensions != null) {
-            tag._extensions = extensions;
-        }
-
-        // append ill-formed variant subtags to private use
-        if (privuseVar != null) {
-            if (privateuse == null) {
-                privateuse = JAVAVARIANT + SEP + privuseVar;
-            } else {
-                privateuse = privateuse + SEP + JAVAVARIANT + SEP + privuseVar.replace(JAVASEP, SEP);
-            }
-        }
-
-        if (privateuse != null) {
-            tag._privateuse = privateuse;
-        } else if (tag._language.length() == 0) {
-            // use "und" if neither language nor privateuse is available
-            tag._language = UNDETERMINED;
-        }
-
-        return tag;
-    }
-
-    private ParseStatus parseString(String str, boolean javaCompatVar) {
-        // Check if the tag is grandfathered
-        String[] gfmap = GRANDFATHERED.get(new AsciiUtil.CaseInsensitiveKey(str));
-        ParseStatus sts;
-        if (gfmap != null) {
-            _grandfathered = gfmap[0];
-            sts = parseLanguageTag(gfmap[1], javaCompatVar);
-            sts.parseLength = str.length();
-        } else {
-            _grandfathered = "";
-            sts = parseLanguageTag(str, javaCompatVar);
-        }
-        return sts;
-    }
-
     /*
-     * Parse Language-Tag, except grandfathered.
-     * 
      * BNF in RFC5464
      *  
      * Language-Tag  = langtag             ; normal language tags
@@ -623,275 +155,566 @@
      * privateuse    = "x" 1*("-" (1*8alphanum))
      * 
      */
-    private ParseStatus parseLanguageTag(String langtag, boolean javaCompat) {
-        ParseStatus sts = new ParseStatus();
-        StringTokenIterator itr = new StringTokenIterator(langtag, SEP);
-        Parser parser = javaCompat ? JAVA_VARIANT_COMPATIBLE_PARSER : DEFAULT_PARSER;
+    public static LanguageTag parse(String languageTag, ParseStatus sts) {
+        if (sts == null) {
+            sts = new ParseStatus();
+        } else {
+            sts.reset();
+        }
 
-        _javaCompatVariants = javaCompat;
+        StringTokenIterator itr;
+
+        // Check if the tag is grandfathered
+        String[] gfmap = GRANDFATHERED.get(new AsciiUtil.CaseInsensitiveKey(languageTag));
+        if (gfmap != null) {
+            // use preferred mapping
+            itr = new StringTokenIterator(gfmap[1], SEP);
+        } else {
+            itr = new StringTokenIterator(languageTag, SEP);
+    }
+
+        LanguageTag tag = new LanguageTag();
 
         // langtag must start with either language or privateuse
-        _language = parser.parseLanguage(itr, sts);
-        if (_language.length() > 0) {
-            _extlangs = parser.parseExtlangs(itr, sts);
-            _script = parser.parseScript(itr, sts);
-            _region = parser.parseRegion(itr, sts);
-            _variants = parser.parseVariants(itr, sts);
-            _extensions = parser.parseExtensions(itr, sts);
-        }
-        _privateuse = parser.parsePrivateuse(itr, sts);
+        if (tag.parseLanguage(itr, sts)) {
+            tag.parseExtlangs(itr, sts);
+            tag.parseScript(itr, sts);
+            tag.parseRegion(itr, sts);
+            tag.parseVariants(itr, sts);
+            tag.parseExtensions(itr, sts);
+    }
+        tag.parsePrivateuse(itr, sts);
 
         if (!itr.isDone() && !sts.isError()) {
             String s = itr.current();
-            sts.errorIndex = itr.currentStart();
+            sts._errorIndex = itr.currentStart();
             if (s.length() == 0) {
-                sts.errorMsg = "Empty subtag";
+                sts._errorMsg = "Empty subtag";
             } else {
-                sts.errorMsg = "Invalid subtag: " + s; 
-            }
-        }
-
-        return sts;
+                sts._errorMsg = "Invalid subtag: " + s; 
+    }
     }
 
-    public static class ParseStatus {
-        int parseLength = 0;
-        int errorIndex = -1;
-        String errorMsg = null;
-
-        public void reset() {
-            parseLength = 0;
-            errorIndex = -1;
-            errorMsg = null;
-        }
-
-        boolean isError() {
-            return (errorIndex >= 0);
-        }
+        return tag;
     }
 
-    static class Parser {
-        private boolean _javaCompatVar;
+    //
+    // Language subtag parsers
+    //
 
-        Parser(boolean javaCompatVar) {
-            _javaCompatVar = javaCompatVar;
-        }
+    private boolean parseLanguage(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
 
-        //
-        // Language subtag parsers
-        //
+        boolean found = false;
 
-        public String parseLanguage(StringTokenIterator itr, ParseStatus sts) {
-            String language = "";
+        String s = itr.current();
+        if (isLanguage(s)) {
+            found = true;
+            _language = s;
+            sts._parseLength = itr.currentEnd();
+            itr.next();
+    }
 
-            if (itr.isDone() || sts.isError()) {
-                return language;
-            }
+        return found;
+    }
 
+    private boolean parseExtlangs(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
+
+        boolean found = false;
+
+        while (!itr.isDone()) {
             String s = itr.current();
-            if (isLanguage(s)) {
-                language = canonicalizeLanguage(s);
-                sts.parseLength = itr.currentEnd();
-                itr.next();
-            }
-            return language;
+            if (!isExtlang(s)) {
+                break;
+        }
+            found = true;
+            if (_extlangs.isEmpty()) {
+                _extlangs = new ArrayList<String>(3);
+    }
+            _extlangs.add(s);
+            sts._parseLength = itr.currentEnd();
+            itr.next();
+
+            if (_extlangs.size() == 3) {
+                // Maximum 3 extlangs
+                break;
+        }
+    }
+
+        return found;
+    }
+
+    private boolean parseScript(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
+
+        boolean found = false;
+
+        String s = itr.current();
+        if (isScript(s)) {
+            found = true;
+            _script = s;
+            sts._parseLength = itr.currentEnd();
+            itr.next();
+    }
+
+        return found;
+    }
+
+    private boolean parseRegion(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
+
+        boolean found = false;
+
+        String s = itr.current();
+        if (isRegion(s)) {
+            found = true;
+            _region = s;
+            sts._parseLength = itr.currentEnd();
+            itr.next();
+    }
+
+        return found;
         }
 
-        public List<String> parseExtlangs(StringTokenIterator itr, ParseStatus sts) {
-            List<String> extlangs = null;
+    private boolean parseVariants(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+        return false;
+    }
 
-            if (itr.isDone() || sts.isError()) {
-                return Collections.emptyList();
+        boolean found = false;
+
+        while (!itr.isDone()) {
+            String s = itr.current();
+            if (!isVariant(s)) {
+                break;
             }
+            found = true;
+            if (_variants.isEmpty()) {
+                _variants = new ArrayList<String>(3);
+            }
+            _variants.add(s);
+            sts._parseLength = itr.currentEnd();
+            itr.next();
+    }
 
+        return found;
+    }
+
+    private boolean parseExtensions(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
+
+        boolean found = false;
+
+        while (!itr.isDone()) {
+            String s = itr.current();
+            if (isExtensionSingleton(s)) {
+                int start = itr.currentStart();
+                String singleton = s;
+                StringBuilder sb = new StringBuilder(singleton);
+
+                itr.next();
+                while (!itr.isDone()) {
+                    s = itr.current();
+                    if (isExtensionSubtag(s)) {
+                        sb.append(SEP).append(s);
+                        sts._parseLength = itr.currentEnd();
+                    } else {
+                        break;
+                    }
+                    itr.next();
+    }
+
+                if (sts._parseLength <= start) {
+                    sts._errorIndex = start;
+                    sts._errorMsg = "Incomplete extension '" + singleton + "'";
+                    break;
+    }
+
+                if (_extensions.size() == 0) {
+                    _extensions = new ArrayList<String>(4);
+                }
+                _extensions.add(sb.toString());
+                found = true;
+            } else {
+                break;
+            }
+        }
+        return found;
+    }
+
+    private boolean parsePrivateuse(StringTokenIterator itr, ParseStatus sts) {
+        if (itr.isDone() || sts.isError()) {
+            return false;
+    }
+
+        boolean found = false;
+
+        String s = itr.current();
+        if (isPrivateusePrefix(s)) {
+            int start = itr.currentStart();
+            StringBuilder sb = new StringBuilder(s);
+
+            itr.next();
             while (!itr.isDone()) {
-                String s = itr.current();
-                if (!isExtlang(s)) {
+                s = itr.current();
+                if (!isPrivateuseSubtag(s)) {
                     break;
-                }
-                if (extlangs == null) {
-                    extlangs = new ArrayList<String>(3);
-                }
-                extlangs.add(canonicalizeExtlang(s));
-                sts.parseLength = itr.currentEnd();
+    }
+                sb.append(SEP).append(s);
+                sts._parseLength = itr.currentEnd();
+
                 itr.next();
+    }
 
-                if (extlangs.size() == 3) {
-                    // Maximum 3 extlangs
-                    break;
-                }
+            if (sts._parseLength <= start) {
+                // need at least 1 private subtag
+                sts._errorIndex = start;
+                sts._errorMsg = "Incomplete privateuse";
+            } else {
+                _privateuse = sb.toString();
+                found = true;
+    }
+    }
+
+        return found;
+    }
+
+    public static LanguageTag parseLocale(BaseLocale baseLocale, LocaleExtensions localeExtensions) {
+        LanguageTag tag = new LanguageTag();
+
+        String language = baseLocale.getLanguage();
+        String script = baseLocale.getScript();
+        String region = baseLocale.getRegion();
+        String variant = baseLocale.getVariant();
+
+        boolean hasSubtag = false;
+
+        String privuseVar = null;   // store ill-formed variant subtags
+
+        if (language.length() > 0 && isLanguage(language)) {
+            // Convert a deprecated language code used by Java to
+            // a new code
+            if (language.equals("iw")) {
+                language = "he";
+            } else if (language.equals("ji")) {
+                language = "yi";
+            } else if (language.equals("in")) {
+                language = "id";
             }
-
-            if (extlangs == null) {
-                return Collections.emptyList();
-            }
-
-            return extlangs;
+            tag._language = language;
         }
 
-        public String parseScript(StringTokenIterator itr, ParseStatus sts) {
-            String script = "";
-
-            if (itr.isDone() || sts.isError()) {
-                return script;
-            }
-
-            String s = itr.current();
-            if (isScript(s)) {
-                script = canonicalizeScript(s);
-                sts.parseLength = itr.currentEnd();
-                itr.next();
-            }
-
-            return script;
+        if (script.length() > 0 && isScript(script)) {
+            tag._script = canonicalizeScript(script);
+            hasSubtag = true;
         }
 
-        public String parseRegion(StringTokenIterator itr, ParseStatus sts) {
-            String region = "";
-
-            if (itr.isDone() || sts.isError()) {
-                return region;
-            }
-
-            String s = itr.current();
-            if (isRegion(s)) {
-                region = canonicalizeRegion(s);
-                sts.parseLength = itr.currentEnd();
-                itr.next();
-            }
-
-            return region;
+        if (region.length() > 0 && isRegion(region)) {
+            tag._region = canonicalizeRegion(region);
+            hasSubtag = true;
         }
 
-        public List<String> parseVariants(StringTokenIterator itr, ParseStatus sts) {
+        if (JDKIMPL) {
+            // Special handling for no_NO_NY - use nn_NO for language tag
+            if (tag._language.equals("no") && tag._region.equals("NO") && variant.equals("NY")) {
+                tag._language = "nn";
+                variant = "";
+            }
+        }
+
+        if (variant.length() > 0) {
             List<String> variants = null;
-
-            if (itr.isDone() || sts.isError()) {
-                return Collections.emptyList();
-            }
-
-            while (!itr.isDone()) {
-                String s = itr.current();
-                if (!isVariant(s)) {
+            StringTokenIterator varitr = new StringTokenIterator(variant, BaseLocale.SEP);
+            while (!varitr.isDone()) {
+                String var = varitr.current();
+                if (!isVariant(var)) {
                     break;
                 }
                 if (variants == null) {
-                    variants = new ArrayList<String>(3);
+                    variants = new ArrayList<String>();
                 }
-                if (_javaCompatVar) {
-                    // preserve casing when Java compatibility option
-                    // is enabled
-                    variants.add(s);
+                if (JDKIMPL) {
+                    variants.add(var);  // Do not canonicalize!
                 } else {
-                    variants.add(canonicalizeVariant(s));
+                    variants.add(canonicalizeVariant(var));
                 }
-                sts.parseLength = itr.currentEnd();
-                itr.next();
+                varitr.next();
             }
-
-            if (variants == null) {
-                return Collections.emptyList();
+            if (variants != null) {
+                tag._variants = variants;
+                hasSubtag = true;
             }
-
-            return variants;
-        }
-
-        public SortedMap<Character, Extension> parseExtensions(StringTokenIterator itr, ParseStatus sts) {
-            SortedMap<Character, Extension> extensionMap = null;
-
-            if (itr.isDone() || sts.isError()) {
-                return EMPTY_EXTENSION_MAP;
-            }
-
-            while (!itr.isDone()) {
-                String s = itr.current();
-                if (!isExtensionSingleton(s)) {
-                    break;
-                }
-                if (!itr.hasNext()) {
-                    sts.errorIndex = itr.currentStart();
-                    sts.errorMsg = "Missing extension subtag for extension :" + s;
-                    break;
-                }
-
-                if (extensionMap == null) {
-                    extensionMap = new TreeMap<Character, Extension>();
-                }
-
-                String singletonStr = canonicalizeExtensionSingleton(s);
-                Character singleton = Character.valueOf(singletonStr.charAt(0));
-
-                if (extensionMap.containsKey(singleton)) {
-                    sts.errorIndex = itr.currentStart();
-                    sts.errorMsg = "Duplicated extension: " + s;
-                    break;
-                }
-
-                itr.next();
-                Extension ext = Extension.create(singleton.charValue(), itr, sts);
-                if (ext != null) {
-                    extensionMap.put(singleton, ext);
-                }
-                if (sts.isError()) {
-                    break;
-                }
-            }
-
-            if (extensionMap == null || extensionMap.size() == 0) {
-                return EMPTY_EXTENSION_MAP;
-            }
-
-            return extensionMap;
-        }
-
-        public String parsePrivateuse(StringTokenIterator itr, ParseStatus sts) {
-            String privateuse = "";
-
-            if (itr.isDone() || sts.isError()) {
-                return privateuse;
-            }
-
-            String s = itr.current();
-            if (isPrivateuseSingleton(s)) {
+            if (!varitr.isDone()) {
+                // ill-formed variant subtags
                 StringBuilder buf = new StringBuilder();
-                int singletonOffset = itr.currentStart();
-                boolean preserveCasing = false;
-                itr.next();
-
-                while (!itr.isDone()) {
-                    s = itr.current();
-                    if (!isPrivateuseSubtag(s)) {
+                while (!varitr.isDone()) {
+                    String prvv = varitr.current();
+                    if (!isPrivateuseSubtag(prvv)) {
+                        // cannot use private use subtag - truncated
                         break;
                     }
-                    if (buf.length() != 0) {
-                         buf.append(SEP);
+                    if (buf.length() > 0) {
+                        buf.append(SEP);
                     }
-                    if (!preserveCasing) {
-                        s = canonicalizePrivateuseSubtag(s);
+                    if (!JDKIMPL) {
+                        prvv = AsciiUtil.toLowerString(prvv);
                     }
-                    buf.append(s);
-                    sts.parseLength = itr.currentEnd();
-
-                    if (_javaCompatVar && s.equals(JAVAVARIANT)) {
-                        // preserve casing after the special
-                        // java reserved private use subtag
-                        // when java compatibility variant option
-                        // is enabled.
-                        preserveCasing = true;
-                    }
-                    itr.next();
+                    buf.append(prvv);
+                    varitr.next();
                 }
-
-                if (buf.length() == 0) {
-                    // need at least 1 private subtag
-                    sts.errorIndex = singletonOffset;
-                    sts.errorMsg = "Incomplete privateuse";
-                } else {
-                    privateuse = buf.toString();
+                if (buf.length() > 0) {
+                    privuseVar = buf.toString();
                 }
             }
-
-            return privateuse;
         }
+
+        List<String> extensions = null;
+        String privateuse = null;
+
+        Set<Character> locextKeys = localeExtensions.getKeys();
+        for (Character locextKey : locextKeys) {
+            Extension ext = localeExtensions.getExtension(locextKey);
+            if (isPrivateusePrefixChar(locextKey.charValue())) {
+                privateuse = ext.getValue();
+            } else {
+                if (extensions == null) {
+                    extensions = new ArrayList<String>();
+                }
+                extensions.add(locextKey.toString() + SEP + ext.getValue());
+            }
+        }
+
+        if (extensions != null) {
+            tag._extensions = extensions;
+            hasSubtag = true;
+        }
+
+        // append ill-formed variant subtags to private use
+        if (privuseVar != null) {
+            if (privateuse == null) {
+                privateuse = PRIVUSE_VARIANT_PREFIX + SEP + privuseVar;
+            } else {
+                privateuse = privateuse + SEP + PRIVUSE_VARIANT_PREFIX + SEP + privuseVar.replace(BaseLocale.SEP, SEP);
+            }
+        }
+
+        if (privateuse != null) {
+            tag._privateuse = privateuse;
+        }
+
+        if (tag._language.length() == 0 && (hasSubtag || privateuse == null)) {
+            // use lang "und" when 1) no language is available AND
+            // 2) any of other subtags other than private use are available or
+            // no private use tag is available
+            tag._language = UNDETERMINED;
+        }
+
+        return tag;
+    }
+
+    //
+    // Getter methods for language subtag fields
+    //
+
+    public String getLanguage() {
+        return _language;
+    }
+
+    public List<String> getExtlangs() {
+        return Collections.unmodifiableList(_extlangs);
+        }
+
+    public String getScript() {
+        return _script;
+        }
+
+    public String getRegion() {
+        return _region;
+    }
+
+    public List<String> getVariants() {
+        return Collections.unmodifiableList(_variants);
+        }
+
+    public List<String> getExtensions() {
+        return Collections.unmodifiableList(_extensions);
+    }
+
+    public String getPrivateuse() {
+        return _privateuse;
+        }
+
+        //
+    // Language subtag syntax checking methods
+        //
+
+    public static boolean isLanguage(String s) {
+        // language      = 2*3ALPHA            ; shortest ISO 639 code
+        //                 ["-" extlang]       ; sometimes followed by
+        //                                     ;   extended language subtags
+        //               / 4ALPHA              ; or reserved for future use
+        //               / 5*8ALPHA            ; or registered language subtag
+        return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaString(s);
+            }
+
+    public static boolean isExtlang(String s) {
+        // extlang       = 3ALPHA              ; selected ISO 639 codes
+        //                 *2("-" 3ALPHA)      ; permanently reserved
+        return (s.length() == 3) && AsciiUtil.isAlphaString(s);
+        }
+
+    public static boolean isScript(String s) {
+        // script        = 4ALPHA              ; ISO 15924 code
+        return (s.length() == 4) && AsciiUtil.isAlphaString(s);
+            }
+
+    public static boolean isRegion(String s) {
+        // region        = 2ALPHA              ; ISO 3166-1 code
+        //               / 3DIGIT              ; UN M.49 code
+        return ((s.length() == 2) && AsciiUtil.isAlphaString(s))
+                || ((s.length() == 3) && AsciiUtil.isNumericString(s));
+                }
+
+    public static boolean isVariant(String s) {
+        // variant       = 5*8alphanum         ; registered variants
+        //               / (DIGIT 3alphanum)
+        int len = s.length();
+        if (len >= 5 && len <= 8) {
+            return AsciiUtil.isAlphaNumericString(s);
+                }
+        if (len == 4) {
+            return AsciiUtil.isNumeric(s.charAt(0))
+                    && AsciiUtil.isAlphaNumeric(s.charAt(1))
+                    && AsciiUtil.isAlphaNumeric(s.charAt(2))
+                    && AsciiUtil.isAlphaNumeric(s.charAt(3));
+            }
+        return false;
+            }
+
+    public static boolean isExtensionSingleton(String s) {
+        // singleton     = DIGIT               ; 0 - 9
+        //               / %x41-57             ; A - W
+        //               / %x59-5A             ; Y - Z
+        //               / %x61-77             ; a - w
+        //               / %x79-7A             ; y - z
+
+        return (s.length() == 1)
+                && AsciiUtil.isAlphaString(s)
+                && !AsciiUtil.caseIgnoreMatch(PRIVATEUSE, s);
+            }
+
+    public static boolean isExtensionSingletonChar(char c) {
+        return isExtensionSingleton(String.valueOf(c));
+            }
+
+    public static boolean isExtensionSubtag(String s) {
+        // extension     = singleton 1*("-" (2*8alphanum))
+        return (s.length() >= 2) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
+        }
+
+    public static boolean isPrivateusePrefix(String s) {
+        // privateuse    = "x" 1*("-" (1*8alphanum))
+        return (s.length() == 1)
+                && AsciiUtil.caseIgnoreMatch(PRIVATEUSE, s);
+            }
+
+    public static boolean isPrivateusePrefixChar(char c) {
+        return (AsciiUtil.caseIgnoreMatch(PRIVATEUSE, String.valueOf(c)));
+            }
+
+    public static boolean isPrivateuseSubtag(String s) {
+        // privateuse    = "x" 1*("-" (1*8alphanum))
+        return (s.length() >= 1) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
+        }
+
+    //
+    // Language subtag canonicalization methods
+    //
+
+    public static String canonicalizeLanguage(String s) {
+        return AsciiUtil.toLowerString(s);
+            }
+
+    public static String canonicalizeExtlang(String s) {
+        return AsciiUtil.toLowerString(s);
+            }
+
+    public static String canonicalizeScript(String s) {
+        return AsciiUtil.toTitleString(s);
+            }
+
+    public static String canonicalizeRegion(String s) {
+        return AsciiUtil.toUpperString(s);
+        }
+
+    public static String canonicalizeVariant(String s) {
+        return AsciiUtil.toLowerString(s);
+            }
+
+    public static String canonicalizeExtension(String s) {
+        return AsciiUtil.toLowerString(s);
+                }
+
+    public static String canonicalizeExtensionSingleton(String s) {
+        return AsciiUtil.toLowerString(s);
+                }
+
+    public static String canonicalizeExtensionSubtag(String s) {
+        return AsciiUtil.toLowerString(s);
+                }
+
+    public static String canonicalizePrivateuse(String s) {
+        return AsciiUtil.toLowerString(s);
+            }
+
+    public static String canonicalizePrivateuseSubtag(String s) {
+        return AsciiUtil.toLowerString(s);
+            }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        if (_language.length() > 0) {
+            sb.append(_language);
+
+            for (String extlang : _extlangs) {
+                sb.append(SEP).append(extlang);
+            }
+
+            if (_script.length() > 0) {
+                sb.append(SEP).append(_script);
+            }
+
+            if (_region.length() > 0) {
+                sb.append(SEP).append(_region);
+                    }
+
+            for (String variant : _extlangs) {
+                sb.append(SEP).append(variant);
+                    }
+
+            for (String extension : _extensions) {
+                sb.append(SEP).append(extension);
+                    }
+                }
+        if (_privateuse.length() > 0) {
+            if (sb.length() > 0) {
+                sb.append(SEP);
+                }
+            sb.append(_privateuse);
+            }
+
+        return sb.toString();
     }
 }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/LocaleExtensions.java b/main/classes/core/src/com/ibm/icu/impl/locale/LocaleExtensions.java
index 83f479a..8d693f7 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/LocaleExtensions.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/LocaleExtensions.java
@@ -1,141 +1,120 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 package com.ibm.icu.impl.locale;
 
 import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.Map.Entry;
+import java.util.TreeSet;
 
-import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
+import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveChar;
+import com.ibm.icu.impl.locale.InternalLocaleBuilder.CaseInsensitiveString;
 
 
 public class LocaleExtensions {
 
-    private SortedMap<Character, Extension> _map = EMPTY_MAP;
-    private String _id = "";
+    private SortedMap<Character, Extension> _map;
+    private String _id;
 
     private static final SortedMap<Character, Extension> EMPTY_MAP =
         Collections.unmodifiableSortedMap(new TreeMap<Character, Extension>());
 
-    private static final LocaleObjectCache<String, LocaleExtensions> LOCALEEXTENSIONS_CACHE =
-        new LocaleObjectCache<String, LocaleExtensions>();
-
-
-    public static LocaleExtensions EMPTY_EXTENSIONS = new LocaleExtensions();
-
+    public static final LocaleExtensions EMPTY_EXTENSIONS;
     public static final LocaleExtensions CALENDAR_JAPANESE;
     public static final LocaleExtensions NUMBER_THAI;
 
     static {
+        EMPTY_EXTENSIONS = new LocaleExtensions();
+        EMPTY_EXTENSIONS._id = "";
+        EMPTY_EXTENSIONS._map = EMPTY_MAP;
+
         CALENDAR_JAPANESE = new LocaleExtensions();
-        CALENDAR_JAPANESE._id = UnicodeLocaleExtension.CA_JAPANESE.getID();
+        CALENDAR_JAPANESE._id = "u-ca-japanese";
         CALENDAR_JAPANESE._map = new TreeMap<Character, Extension>();
-        CALENDAR_JAPANESE._map.put(Character.valueOf(UnicodeLocaleExtension.CA_JAPANESE.getKey()), UnicodeLocaleExtension.CA_JAPANESE);
-        LOCALEEXTENSIONS_CACHE.put(CALENDAR_JAPANESE._id, CALENDAR_JAPANESE);
+        CALENDAR_JAPANESE._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.CA_JAPANESE);
 
         NUMBER_THAI = new LocaleExtensions();
-        NUMBER_THAI._id = UnicodeLocaleExtension.NU_THAI.getID();
+        NUMBER_THAI._id = "u-nu-thai";
         NUMBER_THAI._map = new TreeMap<Character, Extension>();
-        NUMBER_THAI._map.put(Character.valueOf(UnicodeLocaleExtension.NU_THAI.getKey()), UnicodeLocaleExtension.NU_THAI);
-        LOCALEEXTENSIONS_CACHE.put(NUMBER_THAI._id, NUMBER_THAI);
+        NUMBER_THAI._map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), UnicodeLocaleExtension.NU_THAI);
     }
 
-
     private LocaleExtensions() {
     }
 
-    public static LocaleExtensions getInstance(String str) throws LocaleSyntaxException {
-        if (str == null || str.length() == 0) {
-            return EMPTY_EXTENSIONS;
+    /*
+     * Package local constructor, only used by InternalLocaleBuilder.
+     */
+    LocaleExtensions(Map<CaseInsensitiveChar, String> extensions,
+            Set<CaseInsensitiveString> uattributes, Map<CaseInsensitiveString, String> ukeywords) {
+        boolean hasExtension = (extensions != null && extensions.size() > 0);
+        boolean hasUAttributes = (uattributes != null && uattributes.size() > 0);
+        boolean hasUKeywords = (ukeywords != null && ukeywords.size() > 0);
+
+        if (!hasExtension && !hasUAttributes && !hasUKeywords) {
+            _map = EMPTY_MAP;
+            _id = "";
+            return;
         }
-        LocaleExtensions exts = LOCALEEXTENSIONS_CACHE.get(str);
-        if (exts == null) {
-            StringTokenIterator itr = new StringTokenIterator(str, LanguageTag.SEP);
-            ParseStatus sts = new ParseStatus();
-            TreeMap<Character, Extension> map = new TreeMap<Character, Extension>();
 
-            while (!itr.isDone()) {
-                int startOffset = itr.currentEnd();
-                Extension ext = Extension.create(itr, sts);
-                if (sts.isError()) {
-                    throw new LocaleSyntaxException(sts.errorMsg, sts.errorIndex);
+        // Build extension map
+        _map = new TreeMap<Character, Extension>();
+        if (hasExtension) {
+            for (Entry<CaseInsensitiveChar, String> ext : extensions.entrySet()) {
+                char key = AsciiUtil.toLower(ext.getKey().value());
+                String value = ext.getValue();
+
+                if (LanguageTag.isPrivateusePrefixChar(key)) {
+                    // we need to exclude special variant in privuateuse, e.g. "x-abc-lvariant-DEF"
+                    value = InternalLocaleBuilder.removePrivateuseVariant(value);
+                    if (value == null) {
+                        continue;
                 }
-                if (ext == null) {
-                    throw new LocaleSyntaxException("Invalid extension subtag: " + itr.current(), startOffset);
                 }
 
-                Character keyChar = Character.valueOf(ext.getKey());
-                if (map.containsKey(keyChar)) {
-                    throw new LocaleSyntaxException("Duplicated extension: " + keyChar, startOffset);
+                Extension e = new Extension(key, AsciiUtil.toLowerString(value));
+                _map.put(Character.valueOf(key), e);
                 }
-
-                map.put(keyChar, ext);
             }
 
-            String id = toID(map);
-            // check the cache with canonicalized ID
-            exts = LOCALEEXTENSIONS_CACHE.get(id);
-            if (exts == null) {
-                exts = new LocaleExtensions();
-                exts._map = map;
-                exts._id = id;
+        if (hasUAttributes || hasUKeywords) {
+            TreeSet<String> uaset = null;
+            TreeMap<String, String> ukmap = null;
 
-                exts = LOCALEEXTENSIONS_CACHE.put(id, exts);
+            if (hasUAttributes) {
+                uaset = new TreeSet<String>();
+                for (CaseInsensitiveString cis : uattributes) {
+                    uaset.add(AsciiUtil.toLowerString(cis.value()));
             }
         }
-        return exts;
+
+            if (hasUKeywords) {
+                ukmap = new TreeMap<String, String>();
+                for (Entry<CaseInsensitiveString, String> kwd : ukeywords.entrySet()) {
+                    String key = AsciiUtil.toLowerString(kwd.getKey().value());
+                    String type = AsciiUtil.toLowerString(kwd.getValue());
+                    ukmap.put(key, type);
+    }
+        }
+
+            UnicodeLocaleExtension ule = new UnicodeLocaleExtension(uaset, ukmap);
+            _map.put(Character.valueOf(UnicodeLocaleExtension.SINGLETON), ule);
     }
 
-    static LocaleExtensions getInstance(SortedMap<Character, Extension> map) {
-        if (map == null || map.isEmpty()) {
-            return EMPTY_EXTENSIONS;
+        if (_map.size() == 0) {
+            // this could happen when only privuateuse with special variant
+            _map = EMPTY_MAP;
+            _id = "";
+        } else {
+            _id = toID(_map);
         }
-        String id = toID(map);
-        LocaleExtensions exts = LOCALEEXTENSIONS_CACHE.get(id);
-        if (exts == null) {
-            exts = new LocaleExtensions();
-            exts._map = new TreeMap<Character, Extension>(map);
-            exts._id = id;
-
-            exts = LOCALEEXTENSIONS_CACHE.put(id, exts);
-        }
-        return exts;
-    }
-
-    private static String toID(SortedMap<Character, Extension> map) {
-        StringBuilder buf = new StringBuilder();
-        Extension privuse = null;
-        if (map != null && !map.isEmpty()) {
-            Set<Entry<Character, Extension>> entries = map.entrySet();
-            for (Entry<Character, Extension> entry : entries) {
-                Character key = entry.getKey();
-                if (key.charValue() == LanguageTag.PRIVATEUSE.charAt(0)) {
-                    privuse = entry.getValue();
-                    continue;
-                }
-                if (buf.length() > 0) {
-                    buf.append(LanguageTag.SEP);
-                }
-                buf.append(entry.getKey());
-                buf.append(LanguageTag.SEP);
-                buf.append(entry.getValue().getValue());
-            }
-        }
-        if (privuse != null) {
-            if (buf.length() > 0) {
-                buf.append(LanguageTag.SEP);
-            }
-            buf.append(LanguageTag.PRIVATEUSE);
-            buf.append(LanguageTag.SEP);
-            buf.append(privuse.getValue());
-        }
-        return buf.toString();
     }
 
     public Set<Character> getKeys() {
@@ -143,35 +122,81 @@
     }
 
     public Extension getExtension(Character key) {
-        return _map.get(key);
+        return _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
     }
 
     public String getExtensionValue(Character key) {
-        Extension ext = _map.get(key);
+        Extension ext = _map.get(Character.valueOf(AsciiUtil.toLower(key.charValue())));
         if (ext == null) {
-            return "";
+            return null;
         }
         return ext.getValue();
     }
 
+    public Set<String> getUnicodeLocaleAttributes() {
+        Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
+        if (ext == null) {
+            return Collections.emptySet();
+        }
+        assert (ext instanceof UnicodeLocaleExtension);
+        return ((UnicodeLocaleExtension)ext).getUnicodeLocaleAttributes();
+    }
+
     public Set<String> getUnicodeLocaleKeys() {
         Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
         if (ext == null) {
             return Collections.emptySet();
         }
         assert (ext instanceof UnicodeLocaleExtension);
-        return ((UnicodeLocaleExtension)ext).getKeys();
+        return ((UnicodeLocaleExtension)ext).getUnicodeLocaleKeys();
     }
 
     public String getUnicodeLocaleType(String unicodeLocaleKey) {
         Extension ext = _map.get(Character.valueOf(UnicodeLocaleExtension.SINGLETON));
         if (ext == null) {
-            return "";
+            return null;
         }
         assert (ext instanceof UnicodeLocaleExtension);
-        return ((UnicodeLocaleExtension)ext).getType(unicodeLocaleKey);
+        return ((UnicodeLocaleExtension)ext).getUnicodeLocaleType(AsciiUtil.toLowerString(unicodeLocaleKey));
     }
 
+    public boolean isEmpty() {
+        return _map.isEmpty();
+    }
+
+    public static boolean isValidKey(char c) {
+        return LanguageTag.isExtensionSingletonChar(c) || LanguageTag.isPrivateusePrefixChar(c);
+    }
+
+    public static boolean isValidUnicodeLocaleKey(String ukey) {
+        return UnicodeLocaleExtension.isKey(ukey);
+    }
+
+    private static String toID(SortedMap<Character, Extension> map) {
+        StringBuilder buf = new StringBuilder();
+        Extension privuse = null;
+        for (Entry<Character, Extension> entry : map.entrySet()) {
+            char singleton = entry.getKey().charValue();
+            Extension extension = entry.getValue();
+            if (LanguageTag.isPrivateusePrefixChar(singleton)) {
+                privuse = extension;
+            } else {
+                if (buf.length() > 0) {
+                    buf.append(LanguageTag.SEP);
+                }
+                buf.append(extension);
+            }
+        }
+        if (privuse != null) {
+            if (buf.length() > 0) {
+                buf.append(LanguageTag.SEP);
+            }
+            buf.append(privuse);
+        }
+        return buf.toString();
+    }
+
+
     public String toString() {
         return _id;
     }
@@ -184,7 +209,13 @@
         return _id.hashCode();
     }
 
-    public static boolean isValidKey(String key) {
-        return LanguageTag.isExtensionSingleton(key) || LanguageTag.isPrivateuseSingleton(key);
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof LocaleExtensions)) {
+            return false;
+        }
+        return this._id.equals(((LocaleExtensions)other)._id);
     }
 }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/LocaleObjectCache.java b/main/classes/core/src/com/ibm/icu/impl/locale/LocaleObjectCache.java
index c30c872..016aa49 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/LocaleObjectCache.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/LocaleObjectCache.java
@@ -1,77 +1,82 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 package com.ibm.icu.impl.locale;
 
-import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
 import java.util.concurrent.ConcurrentHashMap;
 
-public class LocaleObjectCache<K, V> {
-
-    private ConcurrentHashMap<K, WeakValueRef<V>> _map = new ConcurrentHashMap<K, WeakValueRef<V>>();
-    private ReferenceQueue<V> _rq = new ReferenceQueue<V>();
+public abstract class LocaleObjectCache<K, V> {
+    private ConcurrentHashMap<K, CacheEntry<K, V>> _map;
+    private ReferenceQueue<V> _queue = new ReferenceQueue<V>();
 
     public LocaleObjectCache() {
+        this(16, 0.75f, 16);
     }
 
-    public V get(Object key) {
-        expungeStaleEntries();
-        WeakValueRef<V> ref = _map.get(key);
-        if (ref != null) {
-            return ref.get();
+    public LocaleObjectCache(int initialCapacity, float loadFactor, int concurrencyLevel) {
+        _map = new ConcurrentHashMap<K, CacheEntry<K, V>>(initialCapacity, loadFactor, concurrencyLevel);
         }
+
+    public V get(K key) {
+        V value = null;
+
+        cleanStaleEntries();
+        CacheEntry<K, V> entry = _map.get(key);
+        if (entry != null) {
+            value = entry.get();
+        }
+        if (value == null) {
+            key = normalizeKey(key);
+            V newVal = createObject(key);
+            if (key == null || newVal == null) {
+                // subclass must return non-null key/value object
         return null;
     }
 
-    /*
-     * Unlike Map#put, this method returns non-null value actually
-     * in the cache, even no values for the key was not available
-     * before.
-     */
-    public V put(K key, V value) {
-        expungeStaleEntries();
-        WeakValueRef<V> ref = _map.get(key);
-        if (ref != null) {
-            // Make sure if another thread put the new value
-            V valInCache = ref.get();
-            if (valInCache != null) {
-                return valInCache;
+            CacheEntry<K, V> newEntry = new CacheEntry<K, V>(key, newVal, _queue);
+
+            while (value == null) {
+                cleanStaleEntries();
+                entry = _map.putIfAbsent(key, newEntry);
+                if (entry == null) {
+                    value = newVal;
+                    break;
+                } else {
+                    value = entry.get();
+                }
             }
         }
-        // We do not synchronize the internal map here.
-        // In the worst case, another thread may put the new
-        // value with the same contents, but it should not cause
-        // any serious problem.
-        _map.put(key, new WeakValueRef<V>(key, value, _rq));
         return value;
     }
 
-    private void expungeStaleEntries() {
-        Reference<? extends V> val;
-        while ((val = _rq.poll()) != null) {
-            Object key = ((WeakValueRef<?>)val).getKey();
-            _map.remove(key);
+    @SuppressWarnings("unchecked")
+    private void cleanStaleEntries() {
+        CacheEntry<K, V> entry;
+        while ((entry = (CacheEntry<K, V>)_queue.poll()) != null) {
+            _map.remove(entry.getKey());
         }
     }
 
-    private static class WeakValueRef<V> extends WeakReference<V> {
-        private Object _key;
+    protected abstract V createObject(K key);
 
-        public WeakValueRef(Object key, V value, ReferenceQueue<V> rq) {
-            super(value, rq);
+    protected K normalizeKey(K key) {
+        return key;
+        }
+
+    private static class CacheEntry<K, V> extends SoftReference<V> {
+        private K _key;
+
+        CacheEntry(K key, V value, ReferenceQueue<V> queue) {
+            super(value, queue);
             _key = key;
         }
 
-        public V get() {
-            return super.get();
-        }
-
-        public Object getKey() {
+        K getKey() {
             return _key;
         }
     }
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/ParseStatus.java b/main/classes/core/src/com/ibm/icu/impl/locale/ParseStatus.java
new file mode 100644
index 0000000..6eb5d82
--- /dev/null
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/ParseStatus.java
@@ -0,0 +1,35 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2010-2011, International Business Machines Corporation and    *
+ * others. All Rights Reserved.                                                *
+ *******************************************************************************
+ */
+package com.ibm.icu.impl.locale;
+
+public class ParseStatus {
+    int _parseLength = 0;
+    int _errorIndex = -1;
+    String _errorMsg = null;
+
+    public void reset() {
+        _parseLength = 0;
+        _errorIndex = -1;
+        _errorMsg = null;
+    }
+
+    public boolean isError() {
+        return (_errorIndex >= 0);
+    }
+
+    public int getErrorIndex() {
+        return _errorIndex;
+    }
+
+    public int getParseLength() {
+        return _parseLength;
+    }
+
+    public String getErrorMessage() {
+        return _errorMsg;
+    }
+}
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/PrivateuseExtension.java b/main/classes/core/src/com/ibm/icu/impl/locale/PrivateuseExtension.java
deleted file mode 100644
index 00ab5ee..0000000
--- a/main/classes/core/src/com/ibm/icu/impl/locale/PrivateuseExtension.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
- * others. All Rights Reserved.                                                *
- *******************************************************************************
- */
-package com.ibm.icu.impl.locale;
-
-import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
-
-public class PrivateuseExtension extends Extension {
-    public static final char SINGLETON = 'x';
-
-    protected PrivateuseExtension() {
-        super(SINGLETON);
-    }
-
-    /*
-     * package local constructor only used by LanguageTag implementation
-     */
-    PrivateuseExtension(String privuse) {
-        super(SINGLETON);
-        _value = privuse;
-    }
-
-    protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
-        if (sts.isError() || itr.isDone()) {
-            _value = null;
-            return;
-        }
-
-        StringBuilder buf = new StringBuilder();
-        while (!itr.isDone()) {
-            String s = itr.current();
-            if (!LanguageTag.isPrivateuseSubtag(s)) {
-                break;
-            }
-            s = LanguageTag.canonicalizePrivateuseSubtag(s);
-            if (buf.length() != 0) {
-                buf.append(LanguageTag.SEP);
-            }
-            buf.append(s);
-            sts.parseLength = itr.currentEnd();
-            itr.next();
-        }
-
-        if (buf.length() == 0) {
-            _value = null;
-        } else {
-            _value = buf.toString();
-        }
-    }
-}
diff --git a/main/classes/core/src/com/ibm/icu/impl/locale/UnicodeLocaleExtension.java b/main/classes/core/src/com/ibm/icu/impl/locale/UnicodeLocaleExtension.java
index bbb5709..aa6f987 100644
--- a/main/classes/core/src/com/ibm/icu/impl/locale/UnicodeLocaleExtension.java
+++ b/main/classes/core/src/com/ibm/icu/impl/locale/UnicodeLocaleExtension.java
@@ -1,142 +1,93 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009, International Business Machines Corporation and         *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
 package com.ibm.icu.impl.locale;
 
 import java.util.Collections;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
+import java.util.SortedSet;
 import java.util.TreeMap;
-import java.util.Map.Entry;
-
-import com.ibm.icu.impl.locale.LanguageTag.ParseStatus;
+import java.util.TreeSet;
 
 public class UnicodeLocaleExtension extends Extension {
     public static final char SINGLETON = 'u';
 
-    public static final UnicodeLocaleExtension CA_JAPANESE = new UnicodeLocaleExtension().put("ca", "japanese");
-    public static final UnicodeLocaleExtension NU_THAI = new UnicodeLocaleExtension().put("nu", "thai");
+    private static final SortedSet<String> EMPTY_SORTED_SET = new TreeSet<String>();
+    private static final SortedMap<String, String> EMPTY_SORTED_MAP = new TreeMap<String, String>();
 
-    private SortedMap<String, String> _keyTypeMap;
+    private SortedSet<String> _attributes = EMPTY_SORTED_SET;
+    private SortedMap<String, String> _keywords = EMPTY_SORTED_MAP;
 
-    protected UnicodeLocaleExtension() {
+    public static final UnicodeLocaleExtension CA_JAPANESE;
+    public static final UnicodeLocaleExtension NU_THAI;
+
+    static {
+        CA_JAPANESE = new UnicodeLocaleExtension();
+        CA_JAPANESE._keywords = new TreeMap<String, String>();
+        CA_JAPANESE._keywords.put("ca", "japanese");
+        CA_JAPANESE._value = "ca-japanese";
+
+        NU_THAI = new UnicodeLocaleExtension();
+        NU_THAI._keywords = new TreeMap<String, String>();
+        NU_THAI._keywords.put("nu", "thai");
+        NU_THAI._value = "nu-thai";
+    }
+
+    private UnicodeLocaleExtension() {
         super(SINGLETON);
     }
 
-    /*
-     * Package local constructor only used by InternalLocaleBuilder
-     */
-    UnicodeLocaleExtension(SortedMap<String, String> keyTypeMap) {
-        super(SINGLETON);
-        _keyTypeMap = keyTypeMap;
-        updateStringValue();
-    }
-
-    protected void setExtensionValue(StringTokenIterator itr, ParseStatus sts) {
-        if (sts.isError() || itr.isDone()) {
-            _value = null;
-            return;
+    UnicodeLocaleExtension(SortedSet<String> attributes, SortedMap<String, String> keywords) {
+        this();
+        if (attributes != null && attributes.size() > 0) {
+            _attributes = attributes;
+        }
+        if (keywords != null && keywords.size() > 0) {
+            _keywords = keywords;
         }
 
-        SortedMap<String, String> keyTypeMap = new TreeMap<String, String>();
-        String ukey = null;
-        StringBuilder buf = new StringBuilder();
-        int typeEnd = -1;
+        if (_attributes.size() > 0 || _keywords.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            for (String attribute : _attributes) {
+                sb.append(LanguageTag.SEP).append(attribute);
+                }
+            for (Entry<String, String> keyword : _keywords.entrySet()) {
+                String key = keyword.getKey();
+                String value = keyword.getValue();
 
-        while (!itr.isDone()) {
-            String s = itr.current();
-
-            if (isTypeSubtag(s)) {
-                if (ukey == null) {
-                    // key is expected
-                    sts.errorIndex = itr.currentStart();
-                    sts.errorMsg = "Invalid Unicode locale extension key: " + s;
-                    break;
-                }
-                if (buf.length() > 0) {
-                    buf.append(LanguageTag.SEP);
-                }
-                buf.append(canonicalizeTypeSubtag(s));
-                typeEnd = itr.currentEnd();
-
-                if (!itr.hasNext()) {
-                    // emit the last key/type
-                    keyTypeMap.put(ukey, buf.toString());
-                    sts.parseLength = typeEnd;
-                    itr.next();
-                    break;
-                }
-            } else {
-                // key or others
-                if (ukey != null) {
-                    if (buf.length() > 0) {
-                        // emit previous key and value
-                        keyTypeMap.put(ukey, buf.toString());
-                        sts.parseLength = typeEnd;
-                    } else {
-                        // type is expected
-                        sts.errorIndex = itr.currentStart();
-                        sts.errorMsg = "Invalid Unicode locale extension type: " + s;
-                        break;
+                sb.append(LanguageTag.SEP).append(key);
+                if (value.length() > 0) {
+                    sb.append(LanguageTag.SEP).append(value);
                     }
-                }
-                if (isKey(s)) {
-                    if (itr.hasNext()) {
-                        ukey = canonicalizeKey(s);
-                        if (keyTypeMap.containsKey(ukey)) {
-                            // duplicated key
-                            sts.errorIndex = itr.currentStart();
-                            sts.errorMsg = "Duplicate Unicode locale extension key: " + s;
-                            break;
-                        }
-                        buf.setLength(0);
-                        typeEnd = -1;
-                    } else {
-                        // missing type
-                        sts.errorIndex = itr.currentStart();
-                        sts.errorMsg = "Missing subtag for Unicode locale extension: " + s;
-                        itr.next();
-                        break;
                     }
-                } else {
-                    // others
-                    if (keyTypeMap.size() == 0) {
-                        // key is expected
-                        sts.errorIndex = itr.currentStart();
-                        sts.errorMsg = "Invalid Unicode locale extension key: " + s;
-                    }
-                    break;
+            _value = sb.substring(1);   // skip leading '-'
                 }
             }
-            itr.next();
+
+    public Set<String> getUnicodeLocaleAttributes() {
+        return Collections.unmodifiableSet(_attributes);
         }
 
-        if (keyTypeMap.size() == 0) {
-            _value = null;
-            return;
-        }
-
-        _keyTypeMap = keyTypeMap;
-        updateStringValue();
+    public Set<String> getUnicodeLocaleKeys() {
+        return Collections.unmodifiableSet(_keywords.keySet());
     }
 
-    public Set<String> getKeys() {
-        if (_keyTypeMap == null) {
-            return Collections.emptySet();
-        }
-        return Collections.unmodifiableSet(_keyTypeMap.keySet());
+    public String getUnicodeLocaleType(String unicodeLocaleKey) {
+        return _keywords.get(unicodeLocaleKey);
     }
 
-    public String getType(String key) {
-        String type = null;
-        if (_keyTypeMap != null) {
-            type = _keyTypeMap.get(canonicalizeKey(key));
+    public static boolean isSingletonChar(char c) {
+        return (SINGLETON == AsciiUtil.toLower(c));
         }
 
-        return (type == null ? "" : type);
+    public static boolean isAttribute(String s) {
+        // 3*8alphanum
+        return (s.length() >= 3) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
     }
 
     public static boolean isKey(String s) {
@@ -148,60 +99,4 @@
         // 3*8alphanum
         return (s.length() >= 3) && (s.length() <= 8) && AsciiUtil.isAlphaNumericString(s);
     }
-
-    public static String canonicalizeKey(String s) {
-        return LanguageTag.canonicalizeExtensionSubtag(s);
-    }
-
-    public static String canonicalizeTypeSubtag(String s) {
-        return LanguageTag.canonicalizeExtensionSubtag(s);
-    }
-
-    // These methods are only used by InterlaLocaleBuilder
-    UnicodeLocaleExtension remove(String key) {
-        if (_keyTypeMap != null) {
-            _keyTypeMap.remove(key);
-            updateStringValue();
-        }
-        return this;
-    }
-
-    UnicodeLocaleExtension put(String key, String type) {
-        if (_keyTypeMap == null) {
-            _keyTypeMap = new TreeMap<String, String>();
-        }
-        _keyTypeMap.put(key, type);
-        updateStringValue();
-        return this;
-    }
-
-    boolean isEmpty() {
-        return (_keyTypeMap.size() == 0);
-    }
-
-    private void updateStringValue() {
-        _value = null;
-
-        if (_keyTypeMap != null) {
-            // re-construct string representation
-            StringBuilder valBuf = new StringBuilder();
-            Set<Entry<String, String>> entries = _keyTypeMap.entrySet();
-            boolean isFirst = true;
-            for (Entry<String, String> e : entries) {
-                if (isFirst) {
-                    isFirst = false;
-                } else {
-                    valBuf.append(LanguageTag.SEP);
-                }
-                valBuf.append(e.getKey());
-                valBuf.append(LanguageTag.SEP);
-                valBuf.append(e.getValue());
-            }
-
-            if (valBuf.length() > 0) {
-                _value = valBuf.toString();
-            }
-        }
-    }
 }
-
diff --git a/main/classes/core/src/com/ibm/icu/text/ChineseDateFormatSymbols.java b/main/classes/core/src/com/ibm/icu/text/ChineseDateFormatSymbols.java
index 8e8599b..b5b075d 100644
--- a/main/classes/core/src/com/ibm/icu/text/ChineseDateFormatSymbols.java
+++ b/main/classes/core/src/com/ibm/icu/text/ChineseDateFormatSymbols.java
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (C) 2000-2010, International Business Machines Corporation and
+ * Copyright (C) 2000-2011, International Business Machines Corporation and
  * others. All Rights Reserved.
  ****************************************************************************
  */
@@ -12,6 +12,7 @@
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.ChineseCalendar;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * A subclass of {@link DateFormatSymbols} for {@link ChineseDateFormat}.
@@ -34,11 +35,12 @@
     String isLeapMonth[]; // Do NOT add =null initializer
 
     /**
-     * Construct a ChineseDateFormatSymbols for the default locale.
+     * Construct a ChineseDateFormatSymbols for the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public ChineseDateFormatSymbols() {
-        this(ULocale.getDefault());
+        this(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/CurrencyPluralInfo.java b/main/classes/core/src/com/ibm/icu/text/CurrencyPluralInfo.java
index 5a36064..6586190 100644
--- a/main/classes/core/src/com/ibm/icu/text/CurrencyPluralInfo.java
+++ b/main/classes/core/src/com/ibm/icu/text/CurrencyPluralInfo.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -14,6 +14,7 @@
 
 import com.ibm.icu.impl.CurrencyData;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * This class represents the information needed by
@@ -37,11 +38,12 @@
     private static final long serialVersionUID = 1;
 
     /**
-     * Create a CurrencyPluralInfo object for the default locale.
+     * Create a CurrencyPluralInfo object for the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 4.2
      */
     public CurrencyPluralInfo() {
-        initialize(ULocale.getDefault());
+        initialize(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/DateFormat.java b/main/classes/core/src/com/ibm/icu/text/DateFormat.java
index 4a17ada..ed63a31 100644
--- a/main/classes/core/src/com/ibm/icu/text/DateFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/DateFormat.java
@@ -1,5 +1,5 @@
 /*
- *   Copyright (C) 1996-2010, International Business Machines
+ *   Copyright (C) 1996-2011, International Business Machines
  *   Corporation and others.  All Rights Reserved.
  */
 
@@ -22,6 +22,7 @@
 import com.ibm.icu.util.GregorianCalendar;
 import com.ibm.icu.util.TimeZone;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * {@icuenhanced java.text.DateFormat}.{@icu _usage_}
@@ -907,26 +908,28 @@
 
     /**
      * Gets the time formatter with the default formatting style
-     * for the default locale.
+     * for the default <code>FORMAT</code> locale.
      * @return a time formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getTimeInstance()
     {
-        return get(-1, DEFAULT, ULocale.getDefault());
+        return get(-1, DEFAULT, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Returns the time formatter with the given formatting style
-     * for the default locale.
+     * for the default <code>FORMAT</code> locale.
      * @param style the given formatting style. For example,
      * SHORT for "h:mm a" in the US locale.
      * @return a time formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getTimeInstance(int style)
     {
-        return get(-1, style, ULocale.getDefault());
+        return get(-1, style, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -961,26 +964,28 @@
 
     /**
      * Returns the date formatter with the default formatting style
-     * for the default locale.
+     * for the default <code>FORMAT</code> locale.
      * @return a date formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getDateInstance()
     {
-        return get(DEFAULT, -1, ULocale.getDefault());
+        return get(DEFAULT, -1, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Returns the date formatter with the given formatting style
-     * for the default locale.
+     * for the default <code>FORMAT</code> locale.
      * @param style the given formatting style. For example,
      * SHORT for "M/d/yy" in the US locale.
      * @return a date formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getDateInstance(int style)
     {
-        return get(style, -1, ULocale.getDefault());
+        return get(style, -1, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1015,29 +1020,31 @@
 
     /**
      * Returns the date/time formatter with the default formatting style
-     * for the default locale.
+     * for the default <code>FORMAT</code> locale.
      * @return a date/time formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getDateTimeInstance()
     {
-        return get(DEFAULT, DEFAULT, ULocale.getDefault());
+        return get(DEFAULT, DEFAULT, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Returns the date/time formatter with the given date and time
-     * formatting styles for the default locale.
+     * formatting styles for the default <code>FORMAT</code> locale.
      * @param dateStyle the given date formatting style. For example,
      * SHORT for "M/d/yy" in the US locale.
      * @param timeStyle the given time formatting style. For example,
      * SHORT for "h:mm a" in the US locale.
      * @return a date/time formatter.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static DateFormat getDateTimeInstance(int dateStyle,
                                                        int timeStyle)
     {
-        return get(dateStyle, timeStyle, ULocale.getDefault());
+        return get(dateStyle, timeStyle, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1434,7 +1441,7 @@
      * @stable ICU 2.0
      */
     static final public DateFormat getInstance(Calendar cal) {
-        return getInstance(cal, ULocale.getDefault());
+        return getInstance(cal, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1442,7 +1449,7 @@
      * @stable ICU 2.0
      */
     static final public DateFormat getDateInstance(Calendar cal, int dateStyle) {
-        return getDateInstance(cal, dateStyle, ULocale.getDefault());
+        return getDateInstance(cal, dateStyle, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1450,7 +1457,7 @@
      * @stable ICU 2.0
      */
     static final public DateFormat getTimeInstance(Calendar cal, int timeStyle) {
-        return getTimeInstance(cal, timeStyle, ULocale.getDefault());
+        return getTimeInstance(cal, timeStyle, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1458,7 +1465,7 @@
      * @stable ICU 2.0
      */
     static final public DateFormat getDateTimeInstance(Calendar cal, int dateStyle, int timeStyle) {
-        return getDateTimeInstance(cal, dateStyle, timeStyle, ULocale.getDefault());
+        return getDateTimeInstance(cal, dateStyle, timeStyle, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1466,7 +1473,7 @@
      * @stable ICU 4.0
      */
     public final static DateFormat getPatternInstance(String pattern) {
-        return getPatternInstance(pattern, ULocale.getDefault());
+        return getPatternInstance(pattern, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/DateFormatSymbols.java b/main/classes/core/src/com/ibm/icu/text/DateFormatSymbols.java
index 76b38e0..95cd361 100644
--- a/main/classes/core/src/com/ibm/icu/text/DateFormatSymbols.java
+++ b/main/classes/core/src/com/ibm/icu/text/DateFormatSymbols.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.                                                *
  *******************************************************************************
  */
@@ -24,6 +24,7 @@
 import com.ibm.icu.impl.ZoneStringFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 /**
@@ -126,15 +127,16 @@
 
     /**
      * Constructs a DateFormatSymbols object by loading format data from
-     * resources for the default locale.
+     * resources for the default <code>FORMAT</code> locale.
      *
      * @throws java.util.MissingResourceException if the resources for the default locale
      *          cannot be found or cannot be loaded.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public DateFormatSymbols()
     {
-        this(ULocale.getDefault());
+        this(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java b/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
index 572ecdb..8f54d7b 100644
--- a/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
@@ -1,5 +1,5 @@
 /*
-*   Copyright (C) 2008-2010, International Business Machines
+*   Copyright (C) 2008-2011, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 */
 
@@ -21,6 +21,7 @@
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.DateInterval;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 
 /**
@@ -348,21 +349,22 @@
 
 
     /**
-     * Construct a DateIntervalFormat from skeleton and  the default locale.
+     * Construct a DateIntervalFormat from skeleton and  the default <code>FORMAT</code> locale.
      *
      * This is a convenient override of 
      * getInstance(String skeleton, ULocale locale)  
-     * with the value of locale as default locale.
+     * with the value of locale as default <code>FORMAT</code> locale.
      *
      * @param skeleton  the skeleton on which interval format based.
      * @return          a date time interval formatter.
+     * @see Category#FORMAT
      * @stable ICU 4.0
      */
     public static final DateIntervalFormat 
         getInstance(String skeleton)
                                                  
     {
-        return getInstance(skeleton, ULocale.getDefault());
+        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT));
     }
 
 
@@ -424,21 +426,22 @@
 
     /**
      * Construct a DateIntervalFormat from skeleton
-     *  DateIntervalInfo, and default locale.
+     *  DateIntervalInfo, and default <code>FORMAT</code> locale.
      *
      * This is a convenient override of
      * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf)
-     * with the locale value as default locale.
+     * with the locale value as <code>FORMAT</code> default locale.
      *
      * @param skeleton  the skeleton on which interval format based.
      * @param dtitvinf  the DateIntervalInfo object to be adopted.
      * @return          a date time interval formatter.
+     * @see Category#FORMAT
      * @stable ICU 4.0
      */
     public static final DateIntervalFormat getInstance(String skeleton, 
                                                    DateIntervalInfo dtitvinf)
     {
-        return getInstance(skeleton, ULocale.getDefault(), dtitvinf);
+        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT), dtitvinf);
     }
 
 
diff --git a/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java b/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
index 536bdd5..64dbba6 100644
--- a/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
+++ b/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
@@ -1,6 +1,6 @@
 /*
  ********************************************************************************
- * Copyright (C) 2006-2010, Google, International Business Machines Corporation *
+ * Copyright (C) 2006-2011, Google, International Business Machines Corporation *
  * and others. All Rights Reserved.                                             *
  ********************************************************************************
  */
@@ -26,6 +26,7 @@
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.Freezable;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 /**
@@ -103,11 +104,12 @@
     }
 
     /**
-     * Construct a flexible generator according to data for the default locale.
+     * Construct a flexible generator according to data for the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 3.6
      */
     public static DateTimePatternGenerator getInstance() {
-        return getInstance(ULocale.getDefault());
+        return getInstance(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
index 72e4962..dfc207f9 100644
--- a/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
@@ -31,6 +31,7 @@
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.CurrencyAmount;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * {@icuenhanced java.text.DecimalFormat}.{@icu _usage_}
@@ -601,7 +602,7 @@
 
     /**
      * Creates a DecimalFormat using the default pattern and symbols for the default
-     * locale. This is a convenient way to obtain a DecimalFormat when
+     * <code>FORMAT</code> locale. This is a convenient way to obtain a DecimalFormat when
      * internationalization is not the main concern.
      *
      * <p>To obtain standard formats for a given locale, use the factory methods on
@@ -612,10 +613,11 @@
      * @see NumberFormat#getNumberInstance
      * @see NumberFormat#getCurrencyInstance
      * @see NumberFormat#getPercentInstance
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public DecimalFormat() {
-        ULocale def = ULocale.getDefault();
+        ULocale def = ULocale.getDefault(Category.FORMAT);
         String pattern = getPattern(def, 0);
         // Always applyPattern after the symbols are set
         this.symbols = new DecimalFormatSymbols(def);
@@ -632,7 +634,7 @@
 
     /**
      * Creates a DecimalFormat from the given pattern and the symbols for the default
-     * locale. This is a convenient way to obtain a DecimalFormat when
+     * <code>FORMAT</code> locale. This is a convenient way to obtain a DecimalFormat when
      * internationalization is not the main concern.
      *
      * <p>To obtain standard formats for a given locale, use the factory methods on
@@ -645,11 +647,12 @@
      * @see NumberFormat#getNumberInstance
      * @see NumberFormat#getCurrencyInstance
      * @see NumberFormat#getPercentInstance
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public DecimalFormat(String pattern) {
         // Always applyPattern after the symbols are set
-        ULocale def = ULocale.getDefault();
+        ULocale def = ULocale.getDefault(Category.FORMAT);
         this.symbols = new DecimalFormatSymbols(def);
         setCurrency(Currency.getInstance(def));
         applyPatternWithoutExpandAffix(pattern, false);
diff --git a/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java b/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
index 4c07d90..3a2d371 100644
--- a/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.java
+++ b/main/classes/core/src/com/ibm/icu/text/DecimalFormatSymbols.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.                                                *
  *******************************************************************************
  */
@@ -14,12 +14,13 @@
 import java.util.Locale;
 
 import com.ibm.icu.impl.CurrencyData;
-import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.impl.CurrencyData.CurrencyDisplayInfo;
 import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
 import com.ibm.icu.impl.CurrencyData.CurrencySpacingInfo;
+import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 /**
@@ -40,11 +41,12 @@
  */
 public class DecimalFormatSymbols implements Cloneable, Serializable {
     /**
-     * Creates a DecimalFormatSymbols object for the default locale.
+     * Creates a DecimalFormatSymbols object for the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public DecimalFormatSymbols() {
-        initialize(ULocale.getDefault());
+        initialize(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/DigitList.java b/main/classes/core/src/com/ibm/icu/text/DigitList.java
index aab9a2b..a2f946a 100644
--- a/main/classes/core/src/com/ibm/icu/text/DigitList.java
+++ b/main/classes/core/src/com/ibm/icu/text/DigitList.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.                                                *
  *******************************************************************************
  */
@@ -528,7 +528,7 @@
      * Upon return, count will be less than or equal to maximumDigits.
      * This now performs rounding when maximumDigits is 0, formerly it did not.
      */
-    public final void round(int maximumDigits) {        
+    public final void round(int maximumDigits) {
         // Eliminate digits beyond maximum digits to be displayed.
         // Round up if appropriate.
         // [bnf] rewritten to fix 4179818
@@ -557,12 +557,12 @@
                 ++maximumDigits; // Increment for use as count
             }
             count = maximumDigits;
-            /*Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1"
-              Eliminate trailing zeros. [Richard/GCL]
-            */
-            while (count > 1 && digits[count-1] == '0') {
-                --count;
-            } //[Richard/GCL]
+        }
+        // Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1"
+        // Eliminate trailing zeros. [Richard/GCL]
+        // [dlf] moved outside if block, see ticket #6408
+        while (count > 1 && digits[count-1] == '0') {
+          --count;
         }
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java b/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
index 2474546..0352657 100644
--- a/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2007, International Business Machines
+* Copyright (c) 2004-2011, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -11,6 +11,7 @@
 package com.ibm.icu.text;
 
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * A formatter for Measure objects.  This is an abstract base class.
@@ -46,11 +47,12 @@
 
     /**
      * Return a formatter for CurrencyAmount objects in the default
-     * locale.
+     * <code>FORMAT</code> locale.
      * @return a formatter object
+     * @see Category#FORMAT
      * @stable ICU 3.0
      */
     public static MeasureFormat getCurrencyFormat() {
-        return getCurrencyFormat(ULocale.getDefault());
+        return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
     }
 }
diff --git a/main/classes/core/src/com/ibm/icu/text/MessageFormat.java b/main/classes/core/src/com/ibm/icu/text/MessageFormat.java
index af8d58b..b39552e 100644
--- a/main/classes/core/src/com/ibm/icu/text/MessageFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/MessageFormat.java
@@ -14,6 +14,7 @@
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.text.AttributedCharacterIterator;
+import java.text.AttributedCharacterIterator.Attribute;
 import java.text.AttributedString;
 import java.text.CharacterIterator;
 import java.text.ChoiceFormat;
@@ -21,7 +22,6 @@
 import java.text.Format;
 import java.text.ParseException;
 import java.text.ParsePosition;
-import java.text.AttributedCharacterIterator.Attribute;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -34,6 +34,7 @@
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * {@icuenhanced java.text.MessageFormat}.{@icu _usage_}
@@ -421,7 +422,7 @@
     static final long serialVersionUID = 7136212545847378651L;
 
     /**
-     * Constructs a MessageFormat for the default locale and the
+     * Constructs a MessageFormat for the default <code>FORMAT</code> locale and the
      * specified pattern.
      * The constructor first sets the locale, then parses the pattern and
      * creates a list of subformats for the format elements contained in it.
@@ -430,10 +431,11 @@
      *
      * @param pattern the pattern for this message format
      * @exception IllegalArgumentException if the pattern is invalid
+     * @see Category#FORMAT
      * @stable ICU 3.0
      */
     public MessageFormat(String pattern) {
-        this.ulocale = ULocale.getDefault();
+        this.ulocale = ULocale.getDefault(Category.FORMAT);
         applyPattern(pattern);
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/text/NumberFormat.java b/main/classes/core/src/com/ibm/icu/text/NumberFormat.java
index 9a1f78c..f7d16c9 100644
--- a/main/classes/core/src/com/ibm/icu/text/NumberFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/NumberFormat.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.                                                *
  *******************************************************************************
  */
@@ -25,6 +25,7 @@
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.CurrencyAmount;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 /**
@@ -498,16 +499,17 @@
     //============== Locale Stuff =====================
 
     /**
-     * Returns the default number format for the current default locale.
+     * Returns the default number format for the current default <code>FORMAT</code> locale.
      * The default format is one of the styles provided by the other
      * factory methods: getNumberInstance, getIntegerInstance,
      * getCurrencyInstance or getPercentInstance.
      * Exactly which one is locale-dependent.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     //Bug 4408066 [Richard/GCL]
     public final static NumberFormat getInstance() {
-        return getInstance(ULocale.getDefault(), NUMBERSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), NUMBERSTYLE);
     }
 
     /**
@@ -533,12 +535,13 @@
     }
 
     /**
-     * {@icu} Returns a specific style number format for default locale.
+     * {@icu} Returns a specific style number format for default <code>FORMAT</code> locale.
      * @param style  number format style
+     * @see Category#FORMAT
      * @stable ICU 4.2
      */
     public final static NumberFormat getInstance(int style) {
-        return getInstance(ULocale.getDefault(), style);
+        return getInstance(ULocale.getDefault(Category.FORMAT), style);
     }
 
     /**
@@ -553,11 +556,12 @@
 
 
     /**
-     * Returns a general-purpose number format for the current default locale.
+     * Returns a general-purpose number format for the current default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static NumberFormat getNumberInstance() {
-        return getInstance(ULocale.getDefault(), NUMBERSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), NUMBERSTYLE);
     }
 
     /**
@@ -577,7 +581,7 @@
     }
 
     /**
-     * Returns an integer number format for the current default locale. The
+     * Returns an integer number format for the current default <code>FORMAT</code> locale. The
      * returned number format is configured to round floating point numbers
      * to the nearest integer using IEEE half-even rounding (see {@link
      * com.ibm.icu.math.BigDecimal#ROUND_HALF_EVEN ROUND_HALF_EVEN}) for formatting,
@@ -585,11 +589,12 @@
      * #isParseIntegerOnly isParseIntegerOnly}).
      *
      * @return a number format for integer values
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     //Bug 4408066 [Richard/GCL]
     public final static NumberFormat getIntegerInstance() {
-        return getInstance(ULocale.getDefault(), INTEGERSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), INTEGERSTYLE);
     }
 
     /**
@@ -626,12 +631,13 @@
     }
 
     /**
-     * Returns a currency format for the current default locale.
+     * Returns a currency format for the current default <code>FORMAT</code> locale.
      * @return a number format for currency
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static NumberFormat getCurrencyInstance() {
-        return getInstance(ULocale.getDefault(), CURRENCYSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), CURRENCYSTYLE);
     }
 
     /**
@@ -653,12 +659,13 @@
     }
 
     /**
-     * Returns a percentage format for the current default locale.
+     * Returns a percentage format for the current default <code>FORMAT</code> locale.
      * @return a number format for percents
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static NumberFormat getPercentInstance() {
-        return getInstance(ULocale.getDefault(), PERCENTSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), PERCENTSTYLE);
     }
 
     /**
@@ -680,12 +687,13 @@
     }
 
     /**
-     * {@icu} Returns a scientific format for the current default locale.
+     * {@icu} Returns a scientific format for the current default <code>FORMAT</code> locale.
      * @return a scientific number format
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public final static NumberFormat getScientificInstance() {
-        return getInstance(ULocale.getDefault(), SCIENTIFICSTYLE);
+        return getInstance(ULocale.getDefault(Category.FORMAT), SCIENTIFICSTYLE);
     }
 
     /**
@@ -1197,7 +1205,7 @@
         if (c == null) {
             ULocale uloc = getLocale(ULocale.VALID_LOCALE);
             if (uloc == null) {
-                uloc = ULocale.getDefault();
+                uloc = ULocale.getDefault(Category.FORMAT);
             }
             c = Currency.getInstance(uloc);
         }
diff --git a/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java b/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java
index fb544f2..4de6fc8 100644
--- a/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java
+++ b/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2003-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2003-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -12,10 +12,10 @@
 import java.util.Set;
 
 import com.ibm.icu.impl.ICULocaleService;
-import com.ibm.icu.impl.ICUResourceBundle;
-import com.ibm.icu.impl.ICUService;
 import com.ibm.icu.impl.ICULocaleService.LocaleKey;
 import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
+import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.ICUService;
 import com.ibm.icu.impl.ICUService.Factory;
 import com.ibm.icu.impl.ICUService.Key;
 import com.ibm.icu.text.NumberFormat.NumberFormatFactory;
diff --git a/main/classes/core/src/com/ibm/icu/text/NumberingSystem.java b/main/classes/core/src/com/ibm/icu/text/NumberingSystem.java
index ec46e31..982a337 100644
--- a/main/classes/core/src/com/ibm/icu/text/NumberingSystem.java
+++ b/main/classes/core/src/com/ibm/icu/text/NumberingSystem.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -16,6 +16,7 @@
 import com.ibm.icu.impl.SimpleCache;
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.UResourceBundleIterator;
 
@@ -130,11 +131,12 @@
     }
 
     /**
-     * Returns the default numbering system for the default locale.
+     * Returns the default numbering system for the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 4.2
      */
     public static NumberingSystem getInstance() {
-        return getInstance(ULocale.getDefault());
+        return getInstance(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/PluralFormat.java b/main/classes/core/src/com/ibm/icu/text/PluralFormat.java
index 5688bc8..1826be3 100644
--- a/main/classes/core/src/com/ibm/icu/text/PluralFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/PluralFormat.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2007-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2007-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -16,6 +16,7 @@
 
 import com.ibm.icu.impl.UCharacterProperty;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * <p>
@@ -180,13 +181,14 @@
     private NumberFormat numberFormat = null;
 
     /**
-     * Creates a new <code>PluralFormat</code> for the default locale.
+     * Creates a new <code>PluralFormat</code> for the default <code>FORMAT</code> locale.
      * This locale will be used to get the set of plural rules and for standard
      * number formatting.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public PluralFormat() {
-        init(null, ULocale.getDefault());
+        init(null, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -202,13 +204,14 @@
 
     /**
      * Creates a new <code>PluralFormat</code> for a given set of rules.
-     * The standard number formatting will be done using the default locale.
+     * The standard number formatting will be done using the default <code>FORMAT</code> locale.
      * @param rules defines the behavior of the <code>PluralFormat</code>
      *        object.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public PluralFormat(PluralRules rules) {
-        init(rules, ULocale.getDefault());
+        init(rules, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -226,14 +229,15 @@
 
     /**
      * Creates a new <code>PluralFormat</code> for a given pattern string.
-     * The default locale will be used to get the set of plural rules and for
+     * The default <code>FORMAT</code> locale will be used to get the set of plural rules and for
      * standard number formatting.
      * @param  pattern the pattern for this <code>PluralFormat</code>.
      * @throws IllegalArgumentException if the pattern is invalid.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public PluralFormat(String pattern) {
-        init(null, ULocale.getDefault());
+        init(null, ULocale.getDefault(Category.FORMAT));
         applyPattern(pattern);
     }
 
@@ -257,15 +261,16 @@
     /**
      * Creates a new <code>PluralFormat</code> for a given set of rules and a
      * pattern.
-     * The standard number formatting will be done using the default locale.
+     * The standard number formatting will be done using the default <code>FORMAT</code> locale.
      * @param rules defines the behavior of the <code>PluralFormat</code>
      *        object.
      * @param  pattern the pattern for this <code>PluralFormat</code>.
      * @throws IllegalArgumentException if the pattern is invalid.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public PluralFormat(PluralRules rules, String pattern) {
-        init(rules, ULocale.getDefault());
+        init(rules, ULocale.getDefault(Category.FORMAT));
         applyPattern(pattern);
     }
 
@@ -507,12 +512,13 @@
      *     constructed from {@link #PluralFormat(ULocale)}.
      * @param ulocale the <code>ULocale</code> used to configure the
      *     formatter. If <code>ulocale</code> is <code>null</code>, the
-     *     default locale will be used.
+     *     default <code>FORMAT</code> locale will be used.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public void setLocale(ULocale ulocale) {
         if (ulocale == null) {
-            ulocale = ULocale.getDefault();
+            ulocale = ULocale.getDefault(Category.FORMAT);
         }
         init(null, ulocale);
     }
@@ -553,7 +559,7 @@
      */
     private void parsingFailure(String errorText) {
         // Set PluralFormat to a valid state.
-        init(null, ULocale.getDefault());
+        init(null, ULocale.getDefault(Category.FORMAT));
         throw new IllegalArgumentException(errorText);
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java b/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java
index 1c7e1d7..e8b0e6a 100644
--- a/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.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.                                                *
  *******************************************************************************
  */
@@ -21,6 +21,7 @@
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.impl.UCharacterProperty;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.UResourceBundleIterator;
 
@@ -607,20 +608,21 @@
 
     /**
      * Creates a RuleBasedNumberFormat that behaves according to the description
-     * passed in.  The formatter uses the default locale.
+     * passed in.  The formatter uses the default <code>FORMAT</code> locale.
      * @param description A description of the formatter's desired behavior.
      * See the class documentation for a complete explanation of the description
      * syntax.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public RuleBasedNumberFormat(String description) {
-        locale = ULocale.getDefault();
+        locale = ULocale.getDefault(Category.FORMAT);
         init(description, null);
     }
 
     /**
      * Creates a RuleBasedNumberFormat that behaves according to the description
-     * passed in.  The formatter uses the default locale.
+     * passed in.  The formatter uses the default <code>FORMAT</code> locale.
      * <p>
      * The localizations data provides information about the public
      * rule sets and their localized display names for different
@@ -637,10 +639,11 @@
      * syntax.
      * @param localizations a list of localizations for the rule set
      * names in the description.
+     * @see Category#FORMAT
      * @stable ICU 3.2
      */
     public RuleBasedNumberFormat(String description, String[][] localizations) {
-        locale = ULocale.getDefault();
+        locale = ULocale.getDefault(Category.FORMAT);
         init(description, localizations);
     }
 
@@ -805,17 +808,18 @@
 
     /**
      * Creates a RuleBasedNumberFormat from a predefined description.  Uses the
-     * default locale.
+     * default <code>FORMAT</code> locale.
      * @param format A selector code specifying which kind of formatter to create.
      * There are three legal values: SPELLOUT, which creates a formatter that spells
      * out a value in words in the default locale's langyage, ORDINAL, which attaches
      * an ordinal suffix from the default locale's language to a numeral, and
      * DURATION, which formats a duration in seconds as hours, minutes, and seconds.
      * or NUMBERING_SYSTEM, which is used for alternate numbering systems such as Hebrew.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public RuleBasedNumberFormat(int format) {
-        this(ULocale.getDefault(), format);
+        this(ULocale.getDefault(Category.FORMAT), format);
     }
 
     //-----------------------------------------------------------------------
@@ -911,7 +915,7 @@
         try {
             loc = (ULocale) in.readObject();
         } catch (Exception e) {
-            loc = ULocale.getDefault();
+            loc = ULocale.getDefault(Category.FORMAT);
         }
 
         // build a brand-new RuleBasedNumberFormat from the description,
@@ -963,7 +967,7 @@
 
     private String[] getNameListForLocale(ULocale loc) {
         if (loc != null && ruleSetDisplayNames != null) {
-            String[] localeNames = { loc.getBaseName(), ULocale.getDefault().getBaseName() };
+            String[] localeNames = { loc.getBaseName(), ULocale.getDefault(Category.DISPLAY).getBaseName() };
             for (int i = 0; i < localeNames.length; ++i) {
                 String lname = localeNames[i];
                 while (lname.length() > 0) {
@@ -1001,13 +1005,14 @@
     }
 
     /**
-     * Return the rule set display names for the current default locale.
+     * Return the rule set display names for the current default <code>DISPLAY</code> locale.
      * @return an array of the display names
      * @see #getRuleSetDisplayNames(ULocale)
+     * @see Category#DISPLAY
      * @stable ICU 3.2
      */
     public String[] getRuleSetDisplayNames() {
-        return getRuleSetDisplayNames(ULocale.getDefault());
+        return getRuleSetDisplayNames(ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1034,13 +1039,14 @@
     }
 
     /**
-     * Return the rule set display name for the provided rule set in the current default locale.
+     * Return the rule set display name for the provided rule set in the current default <code>DISPLAY</code> locale.
      * @return the display name for the rule set
      * @see #getRuleSetDisplayName(String,ULocale)
+     * @see Category#DISPLAY
      * @stable ICU 3.2
      */
     public String getRuleSetDisplayName(String ruleSetName) {
-        return getRuleSetDisplayName(ruleSetName, ULocale.getDefault());
+        return getRuleSetDisplayName(ruleSetName, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java b/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java
index 9f4a64d..65469e7 100644
--- a/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.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.                                                *
  *******************************************************************************
  */
@@ -38,6 +38,7 @@
 import com.ibm.icu.util.TimeZone;
 import com.ibm.icu.util.TimeZoneTransition;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 
 
 /**
@@ -361,11 +362,12 @@
     private transient boolean useFastFormat;
 
     /**
-     * Constructs a SimpleDateFormat using the default pattern for the default
+     * Constructs a SimpleDateFormat using the default pattern for the default <code>FORMAT</code>
      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
      * generality, use the factory methods in the DateFormat class.
      *
      * @see DateFormat
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public SimpleDateFormat() {
@@ -373,9 +375,10 @@
     }
 
     /**
-     * Constructs a SimpleDateFormat using the given pattern in the default
+     * Constructs a SimpleDateFormat using the given pattern in the default <code>FORMAT</code>
      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
      * generality, use the factory methods in the DateFormat class.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public SimpleDateFormat(String pattern)
@@ -426,7 +429,7 @@
     /**
      * Constructs a SimpleDateFormat using the given pattern and
      * locale-specific symbol data.
-     * Warning: uses default locale for digits!
+     * Warning: uses default <code>FORMAT</code> locale for digits!
      * @stable ICU 2.0
      */
     public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
@@ -495,7 +498,7 @@
      */
     private void initialize() {
         if (locale == null) {
-            locale = ULocale.getDefault();
+            locale = ULocale.getDefault(Category.FORMAT);
         }
         if (formatData == null) {
             formatData = new DateFormatSymbols(locale);
@@ -536,7 +539,7 @@
      * This method is only used by the default SimpleDateFormat constructor.
      */
     private static synchronized String getDefaultPattern() {
-        ULocale defaultLocale = ULocale.getDefault();
+        ULocale defaultLocale = ULocale.getDefault(Category.FORMAT);
         if (!defaultLocale.equals(cachedDefaultLocale)) {
             cachedDefaultLocale = defaultLocale;
             Calendar cal = Calendar.getInstance(cachedDefaultLocale);
diff --git a/main/classes/core/src/com/ibm/icu/text/TimeUnitFormat.java b/main/classes/core/src/com/ibm/icu/text/TimeUnitFormat.java
index 1b17fb3..ec5cdcd 100644
--- a/main/classes/core/src/com/ibm/icu/text/TimeUnitFormat.java
+++ b/main/classes/core/src/com/ibm/icu/text/TimeUnitFormat.java
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (C) 2008-2010, Google, International Business Machines
+ * Copyright (C) 2008-2011, Google, International Business Machines
  * Corporationand others. All Rights Reserved.
  **************************************************************************
  */
@@ -19,6 +19,7 @@
 import com.ibm.icu.util.TimeUnit;
 import com.ibm.icu.util.TimeUnitAmount;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 
@@ -323,7 +324,7 @@
             if (format != null) {
                 locale = format.getLocale(null);
             } else {
-                locale = ULocale.getDefault();
+                locale = ULocale.getDefault(Category.FORMAT);
             }
         }
         if (format == null) {
diff --git a/main/classes/core/src/com/ibm/icu/util/CECalendar.java b/main/classes/core/src/com/ibm/icu/util/CECalendar.java
index 9e26ac3..dd908e5 100644
--- a/main/classes/core/src/com/ibm/icu/util/CECalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/CECalendar.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.                                                *
  *******************************************************************************
  */
@@ -9,6 +9,8 @@
 import java.util.Date;
 import java.util.Locale;
 
+import com.ibm.icu.util.ULocale.Category;
+
 /**
  * Base class for EthiopicCalendar and CopticCalendar.
  */
@@ -49,20 +51,20 @@
 
     /**
      * Constructs a default <code>CECalendar</code> using the current time
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      */
     protected CECalendar() {
-        this(TimeZone.getDefault(), ULocale.getDefault());
+        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Constructs a <code>CECalendar</code> based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      *
      * @param zone The time zone for the new calendar.
      */
     protected CECalendar(TimeZone zone) {
-        this(zone, ULocale.getDefault());
+        this(zone, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -113,7 +115,7 @@
 
     /**
      * Constructs a <code>CECalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      *
@@ -123,24 +125,24 @@
      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
      */
     protected CECalendar(int year, int month, int date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(year, month, date);
     }
 
     /**
      * Constructs a <code>CECalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param date      The date to which the new calendar is set.
      */
     protected CECalendar(Date date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.setTime(date);
     }
 
     /**
      * Constructs a <code>CECalendar</code> with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
@@ -153,7 +155,7 @@
     protected CECalendar(int year, int month, int date, int hour,
                          int minute, int second)
     {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(year, month, date, hour, minute, second);
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/util/Calendar.java b/main/classes/core/src/com/ibm/icu/util/Calendar.java
index fa635da..783870e 100644
--- a/main/classes/core/src/com/ibm/icu/util/Calendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/Calendar.java
@@ -1,5 +1,5 @@
 /*
-*   Copyright (C) 1996-2010, International Business Machines
+*   Copyright (C) 1996-2011, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 */
 
@@ -26,6 +26,7 @@
 import com.ibm.icu.text.DateFormatSymbols;
 import com.ibm.icu.text.MessageFormat;
 import com.ibm.icu.text.SimpleDateFormat;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * {@icuenhanced java.util.Calendar}.{@icu _usage_}
@@ -1486,13 +1487,14 @@
 
     /**
      * Constructs a Calendar with the default time zone
-     * and locale.
+     * and the default <code>FORMAT</code> locale.
      * @see     TimeZone#getDefault
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     protected Calendar()
     {
-        this(TimeZone.getDefault(), ULocale.getDefault());
+        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -1618,7 +1620,7 @@
      */
     private static Calendar getInstanceInternal(TimeZone tz, ULocale locale) {
         if (locale == null) {
-            locale = ULocale.getDefault();
+            locale = ULocale.getDefault(Category.FORMAT);
         }
         if (tz == null) {
             tz = TimeZone.getDefault();
@@ -2466,6 +2468,11 @@
         // clone the calendar so we don't mess with the real one, and set it to
         // accept anything for the field values
         Calendar work = (Calendar) clone();
+
+        // need to resolve time here, otherwise, fields set for actual limit
+        // may cause conflict with fields previously set (but not yet resolved).
+        work.complete();
+
         work.setLenient(true);
         work.prepareGetActual(field, delta < 0);
 
@@ -2494,7 +2501,6 @@
         } while (startValue != endValue);
 
         return result;
-
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/util/CalendarServiceShim.java b/main/classes/core/src/com/ibm/icu/util/CalendarServiceShim.java
index 6ea42f5..28700b7 100644
--- a/main/classes/core/src/com/ibm/icu/util/CalendarServiceShim.java
+++ b/main/classes/core/src/com/ibm/icu/util/CalendarServiceShim.java
@@ -1,5 +1,5 @@
 /*
-*   Copyright (C) 2007-2009, International Business Machines
+*   Copyright (C) 2007-2011, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 */
 
@@ -10,10 +10,10 @@
 import java.util.Set;
 
 import com.ibm.icu.impl.ICULocaleService;
-import com.ibm.icu.impl.ICUResourceBundle;
-import com.ibm.icu.impl.ICUService;
 import com.ibm.icu.impl.ICULocaleService.LocaleKey;
 import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
+import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.ICUService;
 import com.ibm.icu.impl.ICUService.Factory;
 import com.ibm.icu.impl.ICUService.Key;
 import com.ibm.icu.util.Calendar.CalendarFactory;
diff --git a/main/classes/core/src/com/ibm/icu/util/ChineseCalendar.java b/main/classes/core/src/com/ibm/icu/util/ChineseCalendar.java
index ba45507..3a390bc 100644
--- a/main/classes/core/src/com/ibm/icu/util/ChineseCalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/ChineseCalendar.java
@@ -1,5 +1,5 @@
 /*********************************************************************
- * Copyright (C) 2000-2010, International Business Machines
+ * Copyright (C) 2000-2011, International Business Machines
  * Corporation and others. All Rights Reserved.
  *********************************************************************
  */
@@ -15,6 +15,7 @@
 import com.ibm.icu.impl.CalendarCache;
 import com.ibm.icu.text.ChineseDateFormat;
 import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * <code>ChineseCalendar</code> is a concrete subclass of {@link Calendar}
@@ -153,7 +154,7 @@
 
     /**
      * Constructs a <code>ChineseCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
@@ -161,10 +162,11 @@
      * @param isLeapMonth The value used to set the Chinese calendar's (@link #IS_LEAP_MONTH)
      *                  time field.
      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
+     * @see Category#FORMAT
      * @stable ICU 4.0
      */
     public ChineseCalendar(int year, int month, int isLeapMonth, int date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
 
         // We need to set the current time once to initialize the ChineseCalendar's
         // ERA field to be the current era.
@@ -181,7 +183,7 @@
 
     /**
      * Constructs a <code>ChineseCalendar</code> with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year  the value used to set the {@link #YEAR YEAR} time field in the calendar.
      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
@@ -195,12 +197,13 @@
      *              in the calendar.
      * @param second the value used to set the {@link #SECOND SECOND} time field
      *              in the calendar.
+     * @see Category#FORMAT
      * @stable ICU 4.0
      */
     public ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour,
                              int minute, int second)
     {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
 
         // We need to set the current time once to initialize the ChineseCalendar's
         // ERA field to be the current era.
@@ -231,12 +234,13 @@
 
     /**
      * Construct a <code>ChineseCalendar</code> based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      * @param zone the given time zone
+     * @see Category#FORMAT
      * @stable ICU 4.0
      */
     public ChineseCalendar(TimeZone zone) {
-        super(zone, ULocale.getDefault());
+        super(zone, ULocale.getDefault(Category.FORMAT));
         setTimeInMillis(System.currentTimeMillis());
     }
 
diff --git a/main/classes/core/src/com/ibm/icu/util/Currency.java b/main/classes/core/src/com/ibm/icu/util/Currency.java
index 56f7336..8ea2ee8 100644
--- a/main/classes/core/src/com/ibm/icu/util/Currency.java
+++ b/main/classes/core/src/com/ibm/icu/util/Currency.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.                                                *
  *******************************************************************************
  */
@@ -25,6 +25,7 @@
 import com.ibm.icu.text.CurrencyMetaInfo;
 import com.ibm.icu.text.CurrencyMetaInfo.CurrencyDigits;
 import com.ibm.icu.text.CurrencyMetaInfo.CurrencyFilter;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * A class encapsulating a currency, as defined by ISO 4217.  A
@@ -392,12 +393,13 @@
 
     /**
      * Convenience and compatibility override of getName that
-     * requests the symbol name.
+     * requests the symbol name for the default <code>DISPLAY</code> locale.
      * @see #getName
+     * @see Category#DISPLAY
      * @stable ICU 3.4
      */
     public String getSymbol() {
-        return getSymbol(ULocale.getDefault());
+        return getSymbol(ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/util/GregorianCalendar.java b/main/classes/core/src/com/ibm/icu/util/GregorianCalendar.java
index aa09486..19994d3 100644
--- a/main/classes/core/src/com/ibm/icu/util/GregorianCalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/GregorianCalendar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2010, International Business Machines
+ * Copyright (C) 1996-2011, International Business Machines
  * Corporation and others.  All Rights Reserved.
  */
 
@@ -8,6 +8,8 @@
 import java.util.Date;
 import java.util.Locale;
 
+import com.ibm.icu.util.ULocale.Category;
+
 /**
  * {@icuenhanced java.util.GregorianCalendar}.{@icu _usage_}
  *
@@ -327,21 +329,23 @@
 
     /**
      * Constructs a default GregorianCalendar using the current time
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public GregorianCalendar() {
-        this(TimeZone.getDefault(), ULocale.getDefault());
+        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Constructs a GregorianCalendar based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      * @param zone the given time zone.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public GregorianCalendar(TimeZone zone) {
-        this(zone, ULocale.getDefault());
+        this(zone, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -390,15 +394,16 @@
 
     /**
      * Constructs a GregorianCalendar with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      * @param year the value used to set the YEAR time field in the calendar.
      * @param month the value used to set the MONTH time field in the calendar.
      * Month value is 0-based. e.g., 0 for January.
      * @param date the value used to set the DATE time field in the calendar.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public GregorianCalendar(int year, int month, int date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         set(ERA, AD);
         set(YEAR, year);
         set(MONTH, month);
@@ -407,7 +412,7 @@
 
     /**
      * Constructs a GregorianCalendar with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      * @param year the value used to set the YEAR time field in the calendar.
      * @param month the value used to set the MONTH time field in the calendar.
      * Month value is 0-based. e.g., 0 for January.
@@ -416,11 +421,12 @@
      * in the calendar.
      * @param minute the value used to set the MINUTE time field
      * in the calendar.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public GregorianCalendar(int year, int month, int date, int hour,
                              int minute) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         set(ERA, AD);
         set(YEAR, year);
         set(MONTH, month);
@@ -431,7 +437,7 @@
 
     /**
      * Constructs a GregorianCalendar with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      * @param year the value used to set the YEAR time field in the calendar.
      * @param month the value used to set the MONTH time field in the calendar.
      * Month value is 0-based. e.g., 0 for January.
@@ -442,11 +448,12 @@
      * in the calendar.
      * @param second the value used to set the SECOND time field
      * in the calendar.
+     * @see Category#FORMAT
      * @stable ICU 2.0
      */
     public GregorianCalendar(int year, int month, int date, int hour,
                              int minute, int second) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         set(ERA, AD);
         set(YEAR, year);
         set(MONTH, month);
diff --git a/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java b/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java
index e403c59..864779f 100644
--- a/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/HebrewCalendar.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.                                                *
  *******************************************************************************
  */
@@ -9,6 +9,7 @@
 import java.util.Locale;
 
 import com.ibm.icu.impl.CalendarCache;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * <code>HebrewCalendar</code> is a subclass of <code>Calendar</code>
@@ -274,22 +275,24 @@
 
     /**
      * Constructs a default <code>HebrewCalendar</code> using the current time
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public HebrewCalendar() {
-        this(TimeZone.getDefault(), ULocale.getDefault());
+        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Constructs a <code>HebrewCalendar</code> based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      *
      * @param zone The time zone for the new calendar.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public HebrewCalendar(TimeZone zone) {
-        this(zone, ULocale.getDefault());
+        this(zone, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -344,7 +347,7 @@
 
     /**
      * Constructs a <code>HebrewCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      *
@@ -352,10 +355,11 @@
      *                  The value is 0-based. e.g., 0 for Tishri.
      *
      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public HebrewCalendar(int year, int month, int date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(YEAR, year);
         this.set(MONTH, month);
         this.set(DATE, date);
@@ -363,19 +367,20 @@
 
     /**
      * Constructs a <code>HebrewCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param date      The date to which the new calendar is set.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public HebrewCalendar(Date date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.setTime(date);
     }
 
     /**
      * Constructs a <code>HebrewCalendar</code> with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      *
@@ -389,12 +394,13 @@
      * @param minute    The value used to set the calendar's {@link #MINUTE MINUTE} time field.
      *
      * @param second    The value used to set the calendar's {@link #SECOND SECOND} time field.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public HebrewCalendar(int year, int month, int date, int hour,
                              int minute, int second)
     {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(YEAR, year);
         this.set(MONTH, month);
         this.set(DATE, date);
diff --git a/main/classes/core/src/com/ibm/icu/util/Holiday.java b/main/classes/core/src/com/ibm/icu/util/Holiday.java
index d80a149..fa25250 100644
--- a/main/classes/core/src/com/ibm/icu/util/Holiday.java
+++ b/main/classes/core/src/com/ibm/icu/util/Holiday.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.                                                *
  *******************************************************************************
  */
@@ -12,6 +12,8 @@
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
+import com.ibm.icu.util.ULocale.Category;
+
 /**
  * <b>Note:</b> The Holiday framework is a technology preview.
  * Despite its age, is still draft API, and clients should treat it as such.
@@ -28,7 +30,7 @@
      */
     public static Holiday[] getHolidays()
     {
-        return getHolidays(ULocale.getDefault());
+        return getHolidays(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -138,12 +140,13 @@
     }
 
     /**
-     * Return the name of this holiday in the language of the default locale.
+     * Return the name of this holiday in the language of the default <code>DISPLAY</code> locale.
+     * @see Category#DISPLAY
      * @draft ICU 2.8
      * @provisional This API might change or be removed in a future release.
      */
     public String getDisplayName() {
-        return getDisplayName(ULocale.getDefault());
+        return getDisplayName(ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/util/IndianCalendar.java b/main/classes/core/src/com/ibm/icu/util/IndianCalendar.java
index 74dd50b..eef9377 100644
--- a/main/classes/core/src/com/ibm/icu/util/IndianCalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/IndianCalendar.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.                                                *
  *******************************************************************************
  */
@@ -10,6 +10,8 @@
 import java.util.Date;
 import java.util.Locale;
 
+import com.ibm.icu.util.ULocale.Category;
+
 /**
  * <code>IndianCalendar</code> is a subclass of <code>GregorianCalendar</code>
  * that numbers years since the birth of the Buddha.  This is the civil calendar
@@ -153,22 +155,24 @@
     
     /**
      * Constructs a <code>IndianCalendar</code> using the current time
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public IndianCalendar() {
-       this(TimeZone.getDefault(), ULocale.getDefault());
+       this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Constructs a <code>IndianCalendar</code> based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      *
      * @param zone the given time zone.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public IndianCalendar(TimeZone zone) {
-       this(zone, ULocale.getDefault());
+       this(zone, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -223,19 +227,20 @@
 
     /**
      * Constructs a <code>IndianCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param date      The date to which the new calendar is set.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public IndianCalendar(Date date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.setTime(date);
     }
 
     /**
      * Constructs a <code>IndianCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      *
@@ -243,10 +248,11 @@
      *                  The value is 0-based. e.g., 0 for January.
      *
      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public IndianCalendar(int year, int month, int date) {
-       super(TimeZone.getDefault(), ULocale.getDefault());
+       super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
        this.set(Calendar.YEAR, year);
        this.set(Calendar.MONTH, month);
        this.set(Calendar.DATE, date);
@@ -255,7 +261,7 @@
 
     /**
      * Constructs a IndianCalendar with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
      *
@@ -269,12 +275,13 @@
      * @param minute    The value used to set the calendar's {@link #MINUTE MINUTE} time field.
      *
      * @param second    The value used to set the calendar's {@link #SECOND SECOND} time field.
+     * @see Category#FORMAT
      * @stable ICU 3.8
      */
     public IndianCalendar(int year, int month, int date, int hour,
                              int minute, int second)
     {
-       super(TimeZone.getDefault(), ULocale.getDefault());
+       super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
        this.set(Calendar.YEAR, year);
        this.set(Calendar.MONTH, month);
        this.set(Calendar.DATE, date);
@@ -435,6 +442,12 @@
        //month is 0 based; converting it to 1-based 
        int imonth;
        
+       // If the month is out of range, adjust it into range, and adjust the extended year accordingly
+       if (month < 0 || month > 11) {
+           year += month / 12;
+           month %= 12;
+       }
+       
        if(month == 12) {
            imonth = 1;
        } else {
diff --git a/main/classes/core/src/com/ibm/icu/util/IslamicCalendar.java b/main/classes/core/src/com/ibm/icu/util/IslamicCalendar.java
index 554ada1..2401e86 100644
--- a/main/classes/core/src/com/ibm/icu/util/IslamicCalendar.java
+++ b/main/classes/core/src/com/ibm/icu/util/IslamicCalendar.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.                                                *
  *******************************************************************************
  */
@@ -10,6 +10,7 @@
 
 import com.ibm.icu.impl.CalendarAstronomer;
 import com.ibm.icu.impl.CalendarCache;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * <code>IslamicCalendar</code> is a subclass of <code>Calendar</code>
@@ -163,23 +164,25 @@
 
     /**
      * Constructs a default <code>IslamicCalendar</code> using the current time
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public IslamicCalendar()
     {
-        this(TimeZone.getDefault(), ULocale.getDefault());
+        this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
     }
 
     /**
      * Constructs an <code>IslamicCalendar</code> based on the current time
-     * in the given time zone with the default locale.
+     * in the given time zone with the default <code>FORMAT</code> locale.
      * @param zone the given time zone.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public IslamicCalendar(TimeZone zone)
     {
-        this(zone, ULocale.getDefault());
+        this(zone, ULocale.getDefault(Category.FORMAT));
     }
 
     /**
@@ -236,29 +239,31 @@
 
     /**
      * Constructs an <code>IslamicCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param date      The date to which the new calendar is set.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public IslamicCalendar(Date date) {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.setTime(date);
     }
 
     /**
      * Constructs an <code>IslamicCalendar</code> with the given date set
-     * in the default time zone with the default locale.
+     * in the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year the value used to set the {@link #YEAR YEAR} time field in the calendar.
      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
      *              Note that the month value is 0-based. e.g., 0 for Muharram.
      * @param date the value used to set the {@link #DATE DATE} time field in the calendar.
+     * @see Category#FORMAT
      * @stable ICU 2.8
      */
     public IslamicCalendar(int year, int month, int date)
     {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(Calendar.YEAR, year);
         this.set(Calendar.MONTH, month);
         this.set(Calendar.DATE, date);
@@ -266,7 +271,7 @@
 
     /**
      * Constructs an <code>IslamicCalendar</code> with the given date
-     * and time set for the default time zone with the default locale.
+     * and time set for the default time zone with the default <code>FORMAT</code> locale.
      *
      * @param year  the value used to set the {@link #YEAR YEAR} time field in the calendar.
      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
@@ -278,12 +283,13 @@
      *              in the calendar.
      * @param second the value used to set the {@link #SECOND SECOND} time field
      *              in the calendar.
+     * @see Category#FORMAT              
      * @stable ICU 2.8
      */
     public IslamicCalendar(int year, int month, int date, int hour,
                              int minute, int second)
     {
-        super(TimeZone.getDefault(), ULocale.getDefault());
+        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
         this.set(Calendar.YEAR, year);
         this.set(Calendar.MONTH, month);
         this.set(Calendar.DATE, date);
diff --git a/main/classes/core/src/com/ibm/icu/util/LocaleData.java b/main/classes/core/src/com/ibm/icu/util/LocaleData.java
index 419838a..5b482a7 100644
--- a/main/classes/core/src/com/ibm/icu/util/LocaleData.java
+++ b/main/classes/core/src/com/ibm/icu/util/LocaleData.java
@@ -1,6 +1,6 @@
 /*
  **************************************************************************************
- * Copyright (C) 2009-2010, Google, Inc.; International Business Machines Corporation *
+ * Copyright (C) 2009-2011, Google, Inc.; International Business Machines Corporation *
  * and others. All Rights Reserved.                                                   *
  **************************************************************************************
  */
@@ -10,6 +10,7 @@
 
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.text.UnicodeSet;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * A class for accessing miscelleneous data in the locale bundles
@@ -152,13 +153,14 @@
     }
     
     /**
-     * Gets the LocaleData object associated with the default locale
+     * Gets the LocaleData object associated with the default <code>FORMAT</code> locale
      *
      * @return          A locale data object.
+     * @see Category#FORMAT
      * @stable ICU 3.4
      */
     public static final LocaleData getInstance() {
-       return LocaleData.getInstance(ULocale.getDefault());
+       return LocaleData.getInstance(ULocale.getDefault(Category.FORMAT));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/util/TimeZone.java b/main/classes/core/src/com/ibm/icu/util/TimeZone.java
index ee80173..4455099 100644
--- a/main/classes/core/src/com/ibm/icu/util/TimeZone.java
+++ b/main/classes/core/src/com/ibm/icu/util/TimeZone.java
@@ -1,7 +1,7 @@
 /*
  * @(#)TimeZone.java    1.51 00/01/19
  *
- * Copyright (C) 1996-2010, International Business Machines
+ * Copyright (C) 1996-2011, International Business Machines
  * Corporation and others.  All Rights Reserved.
  */
 
@@ -21,6 +21,7 @@
 import com.ibm.icu.impl.TimeZoneAdapter;
 import com.ibm.icu.impl.ZoneMeta;
 import com.ibm.icu.text.SimpleDateFormat;
+import com.ibm.icu.util.ULocale.Category;
 
 /**
  * {@icuenhanced java.util.TimeZone}.{@icu _usage_}
@@ -343,15 +344,16 @@
 
     /**
      * Returns a name of this time zone suitable for presentation to the user
-     * in the default locale.
+     * in the default <code>DISPLAY</code> locale.
      * This method returns the long generic name.
      * If the display name is not available for the locale,
      * a fallback based on the country, city, or time zone id will be used.
      * @return the human-readable name of this time zone in the default locale.
+     * @see Category#DISPLAY
      * @stable ICU 2.0
      */
     public final String getDisplayName() {
-        return _getDisplayName(false, LONG_GENERIC, ULocale.getDefault());
+        return _getDisplayName(false, LONG_GENERIC, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
@@ -386,7 +388,7 @@
 
     /**
      * Returns a name of this time zone suitable for presentation to the user
-     * in the default locale.
+     * in the default <code>DISPLAY</code> locale.
      * If the display name is not available for the locale,
      * then this method returns a string in the format
      * <code>GMT[+-]hh:mm</code>.
@@ -396,10 +398,11 @@
      * <code>LONG_GENERIC</code>, <code>SHORT_GMT</code>, <code>LONG_GMT</code>,
      * <code>SHORT_COMMONLY_USED</code> or <code>GENERIC_LOCATION</code>.
      * @return the human-readable name of this time zone in the default locale.
+     * @see Category#DISPLAY
      * @stable ICU 2.0
      */
     public final String getDisplayName(boolean daylight, int style) {
-        return getDisplayName(daylight, style, ULocale.getDefault());
+        return getDisplayName(daylight, style, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/classes/core/src/com/ibm/icu/util/ULocale.java b/main/classes/core/src/com/ibm/icu/util/ULocale.java
index 2eb7c2e..6b2087c 100644
--- a/main/classes/core/src/com/ibm/icu/util/ULocale.java
+++ b/main/classes/core/src/com/ibm/icu/util/ULocale.java
@@ -1,6 +1,6 @@
 /*
 ******************************************************************************
-* Copyright (C) 2003-2010, International Business Machines Corporation and   *
+* Copyright (C) 2003-2011, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
@@ -8,13 +8,18 @@
 package com.ibm.icu.util;
 
 import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.text.ParseException;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.MissingResourceException;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 
 import com.ibm.icu.impl.ICUCache;
 import com.ibm.icu.impl.ICUResourceBundle;
@@ -30,6 +35,7 @@
 import com.ibm.icu.impl.locale.LanguageTag;
 import com.ibm.icu.impl.locale.LocaleExtensions;
 import com.ibm.icu.impl.locale.LocaleSyntaxException;
+import com.ibm.icu.impl.locale.ParseStatus;
 import com.ibm.icu.impl.locale.UnicodeLocaleExtension;
 import com.ibm.icu.text.LocaleDisplayNames;
 import com.ibm.icu.text.LocaleDisplayNames.DialectHandling;
@@ -239,12 +245,33 @@
     // default empty locale
     private static final Locale EMPTY_LOCALE = new Locale("", "");
 
+    // special keyword key for Unicode locale attributes
+    private static final String LOCALE_ATTRIBUTE_KEY = "attribute";
+
     /**
      * The root ULocale.
      * @stable ICU 2.8
      */
     public static final ULocale ROOT = new ULocale("", EMPTY_LOCALE);
 
+    /**
+     * Enum for locale categories. These locale categories are used to get/set the default locale for
+     * the specific functionality represented by the category.
+     * @stable ICU 49
+     */
+    public enum Category {
+        /**
+         * Category used to represent the default locale for displaying user interfaces.
+         * @stable ICU 49
+         */
+        DISPLAY,
+        /**
+         * Category used to represent the default locale for formatting date, number and/or currency.
+         * @stable ICU 49
+         */
+        FORMAT
+    }
+
     private static final SimpleCache<Locale, ULocale> CACHE = new SimpleCache<Locale, ULocale>();
 
     /**
@@ -355,21 +382,6 @@
         }
     }
 
-    /*
-     * This table is used for mapping between ICU and special Java
-     * locales.  When an ICU locale matches <minumum base> with
-     * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
-     * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
-     * are mapped to Java locale "ja_JP_JP".  ICU locale "nn" is mapped
-     * to Java locale "no_NO_NY".
-     */
-    private static final String[][] _javaLocaleMap = {
-    //  { <Java>,       <ICU base>, <keyword>,  <value>,    <minimum base>
-        { "ja_JP_JP",   "ja_JP",    "calendar", "japanese", "ja"},
-        { "no_NO_NY",   "nn_NO",    null,       null,       "nn"},
-        { "th_TH_TH",   "th_TH",    "numbers",  "thai",     "th"},
-    };
-
     /**
      * Private constructor used by static initializers.
      */
@@ -399,24 +411,7 @@
         }
         ULocale result = CACHE.get(loc);
         if (result == null) {
-            if (defaultULocale != null && loc == defaultULocale.locale) {
-            result = defaultULocale;
-        } else {
-                String locStr = loc.toString();
-                if (locStr.length() == 0) {
-                    result = ROOT;
-                } else {
-                    for (int i = 0; i < _javaLocaleMap.length; i++) {
-                        if (_javaLocaleMap[i][0].equals(locStr)) {
-                            LocaleIDParser p = new LocaleIDParser(_javaLocaleMap[i][1]);
-                            p.setKeywordValue(_javaLocaleMap[i][2], _javaLocaleMap[i][3]);
-                            locStr = p.getName();
-                            break;
-                        }
-                    }
-                    result = new ULocale(locStr, loc);
-                }
-            }
+            result = JDKLocaleHelper.toULocale(loc);
             CACHE.put(loc, result);
         }
         return result;
@@ -522,45 +517,63 @@
      */
     public Locale toLocale() {
         if (locale == null) {
-            LocaleIDParser p = new LocaleIDParser(localeID);
-            String base = p.getBaseName();
-            for (int i = 0; i < _javaLocaleMap.length; i++) {
-                if (base.equals(_javaLocaleMap[i][1]) || base.equals(_javaLocaleMap[i][4])) {
-                    if (_javaLocaleMap[i][2] != null) {
-                        String val = p.getKeywordValue(_javaLocaleMap[i][2]);
-                        if (val != null && val.equals(_javaLocaleMap[i][3])) {
-                            p = new LocaleIDParser(_javaLocaleMap[i][0]);
-                            break;
-                        }
-                    } else {
-                        p = new LocaleIDParser(_javaLocaleMap[i][0]);
-                        break;
-                    }
-                }
-            }
-            String[] names = p.getLanguageScriptCountryVariant();
-            locale = new Locale(names[0], names[2], names[3]);
+            locale = JDKLocaleHelper.toLocale(this);
         }
         return locale;
     }
 
     private static ICUCache<String, String> nameCache = new SimpleCache<String, String>();
+
     /**
      * Keep our own default ULocale.
      */
     private static Locale defaultLocale = Locale.getDefault();
-    private static ULocale defaultULocale = new ULocale(defaultLocale);
+    private static ULocale defaultULocale = forLocale(defaultLocale);
+
+    private static Locale[] defaultCategoryLocales = new Locale[Category.values().length];
+    private static ULocale[] defaultCategoryULocales = new ULocale[Category.values().length];
+
+    static {
+        for (Category cat: Category.values()) {
+            int idx = cat.ordinal();
+            defaultCategoryLocales[idx] = JDKLocaleHelper.getDefault(cat);
+            defaultCategoryULocales[idx] = forLocale(defaultCategoryLocales[idx]);
+        }
+    }
 
     /**
      * Returns the current default ULocale.
+     * @return the default ULocale.
      * @stable ICU 2.8
      */
     public static ULocale getDefault() {
         synchronized (ULocale.class) {
+            if (defaultULocale == null) {
+                // When Java's default locale has extensions (such as ja-JP-u-ca-japanese),
+                // Locale -> ULocale mapping requires BCP47 keyword mapping data that is currently
+                // stored in a resource bundle. However, UResourceBundle currently requires
+                // non-null default ULocale. For now, this implementation returns ULocale.ROOT
+                // to avoid the problem.
+
+                // TODO: Consider moving BCP47 mapping data out of resource bundle later.
+
+                return ULocale.ROOT;
+            }
             Locale currentDefault = Locale.getDefault();
             if (!defaultLocale.equals(currentDefault)) {
                 defaultLocale = currentDefault;
-                defaultULocale = new ULocale(defaultLocale);
+                defaultULocale = forLocale(currentDefault);
+
+                if (!JDKLocaleHelper.isJava7orNewer()) {
+                    // Detected Java default Locale change.
+                    // We need to update category defaults to match the
+                    // Java 7's behavior on Java 6 or older environment.
+                    for (Category cat : Category.values()) {
+                        int idx = cat.ordinal();
+                        defaultCategoryLocales[idx] = currentDefault;
+                        defaultCategoryULocales[idx] = forLocale(currentDefault);
+                    }
+                }
             }
             return defaultULocale;
         }
@@ -571,17 +584,101 @@
      * If the caller does not have write permission to the
      * user.language property, a security exception will be thrown,
      * and the default ULocale will remain unchanged.
+     * <p>
+     * By setting the default ULocale with this method, all of the default categoy locales
+     * are also set to the specified default ULocale.
      * @param newLocale the new default locale
      * @throws SecurityException if a security manager exists and its
      *        <code>checkPermission</code> method doesn't allow the operation.
      * @throws NullPointerException if <code>newLocale</code> is null
      * @see SecurityManager#checkPermission(java.security.Permission)
      * @see java.util.PropertyPermission
+     * @see ULocale#setDefault(Category, ULocale)
      * @stable ICU 3.0
      */
     public static synchronized void setDefault(ULocale newLocale){
-        Locale.setDefault(newLocale.toLocale());
+        defaultLocale = newLocale.toLocale();
+        Locale.setDefault(defaultLocale);
         defaultULocale = newLocale;
+        // This method also updates all category default locales
+        for (Category cat : Category.values()) {
+            setDefault(cat, newLocale);
+        }
+    }
+
+    /**
+     * Returns the current default ULocale for the specified category.
+     * 
+     * @param category the category
+     * @return the default ULocale for the specified category.
+     * @stable ICU 49
+     */
+    public static ULocale getDefault(Category category) {
+        synchronized (ULocale.class) {
+            int idx = category.ordinal();
+            if (defaultCategoryULocales[idx] == null) {
+                // Just in case this method is called during ULocale class
+                // initialization. Unlike getDefault(), we do not have
+                // cyclic dependency for category default.
+                return ULocale.ROOT;
+            }
+            if (JDKLocaleHelper.isJava7orNewer()) {
+                Locale currentCategoryDefault = JDKLocaleHelper.getDefault(category);
+                if (!defaultCategoryLocales[idx].equals(currentCategoryDefault)) {
+                    defaultCategoryLocales[idx] = currentCategoryDefault;
+                    defaultCategoryULocales[idx] = forLocale(currentCategoryDefault);
+                }
+            } else {
+                // java.util.Locale.setDefault(Locale) in Java 7 updates
+                // category locale defaults. On Java 6 or older environment,
+                // ICU4J checks if the default locale has changed and update
+                // category ULocales here if necessary.
+                
+                // Note: When java.util.Locale.setDefault(Locale) is called
+                // with a Locale same with the previous one, Java 7 still
+                // updates category locale defaults. On Java 6 or older env,
+                // there is no good way to detect the event, ICU4J simply
+                // check if the default Java Locale has changed since last
+                // time.
+
+                Locale currentDefault = Locale.getDefault();
+                if (!defaultLocale.equals(currentDefault)) {
+                    defaultLocale = currentDefault;
+                    defaultULocale = forLocale(currentDefault);
+
+                    for (Category cat : Category.values()) {
+                        int tmpIdx = cat.ordinal();
+                        defaultCategoryLocales[tmpIdx] = currentDefault;
+                        defaultCategoryULocales[tmpIdx] = forLocale(currentDefault);
+                    }
+                }
+                
+                // No synchronization with JDK Locale, because category default
+                // is not supported in Java 6 or older versions
+            }
+            return defaultCategoryULocales[idx];
+        }
+    }
+
+    /**
+     * Sets the default <code>ULocale</code> for the specified <code>Category</code>.
+     * This also sets the default <code>Locale</code> for the specified <code>Category</code>
+     * of the JVM. If the caller does not have write permission to the
+     * user.language property, a security exception will be thrown,
+     * and the default ULocale for the specified Category will remain unchanged.
+     * 
+     * @param category the specified category to set the default locale
+     * @param newLocale the new default locale
+     * @see SecurityManager#checkPermission(java.security.Permission)
+     * @see java.util.PropertyPermission
+     * @stable ICU 49
+     */
+    public static synchronized void setDefault(Category category, ULocale newLocale) {
+        Locale newJavaDefault = newLocale.toLocale();
+        int idx = category.ordinal();
+        defaultCategoryULocales[idx] = newLocale;
+        defaultCategoryLocales[idx] = newJavaDefault;
+        JDKLocaleHelper.setDefault(category, newJavaDefault);
     }
 
     /**
@@ -817,6 +914,36 @@
     }
 
     /**
+     * Gets the shortest length subtag's size.
+     *
+     * @param localeID
+     * @return The size of the shortest length subtag
+     **/
+    private static int getShortestSubtagLength(String localeID) {
+        int localeIDLength = localeID.length();
+        int length = localeIDLength;
+        boolean reset = true;
+        int tmpLength = 0;
+        
+        for (int i = 0; i < localeIDLength; i++) {
+            if (localeID.charAt(i) != '_' && localeID.charAt(i) != '-') {
+                if (reset) {
+                    reset = false;
+                    tmpLength = 0;
+                }
+                tmpLength++;
+            } else {
+                if (tmpLength != 0 && tmpLength < length) {
+                    length = tmpLength;
+                }
+                reset = true;
+            }
+        }
+        
+        return length;
+    }
+
+    /**
      * {@icu} Returns the (normalized) full name for the specified locale.
      *
      * @param localeID the localeID as a string
@@ -824,10 +951,20 @@
      * @stable ICU 3.0
      */
     public static String getName(String localeID){
-        String name = nameCache.get(localeID);
+        String tmpLocaleID;
+        // Convert BCP47 id if necessary
+        if (localeID != null && !localeID.contains("@") && getShortestSubtagLength(localeID) == 1) {
+            tmpLocaleID = forLanguageTag(localeID).getName();
+            if (tmpLocaleID.length() == 0) {
+                tmpLocaleID = localeID;
+            }
+        } else {
+            tmpLocaleID = localeID;
+        }
+        String name = nameCache.get(tmpLocaleID);
         if (name == null) {
-            name = new LocaleIDParser(localeID).getName();
-            nameCache.put(localeID, name);
+            name = new LocaleIDParser(tmpLocaleID).getName();
+            nameCache.put(tmpLocaleID, name);
         }
         return name;
     }
@@ -1052,12 +1189,13 @@
     // display names
 
     /**
-     * Returns this locale's language localized for display in the default locale.
+     * Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
      * @return the localized language name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayLanguage() {
-        return getDisplayLanguageInternal(this, getDefault(), false);
+        return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), false);
     }
 
     /**
@@ -1095,14 +1233,15 @@
         return getDisplayLanguageInternal(new ULocale(localeID), displayLocale, false);
     }
     /**
-     * {@icu} Returns this locale's language localized for display in the default locale.
+     * {@icu} Returns this locale's language localized for display in the default <code>DISPLAY</code> locale.
      * If a dialect name is present in the data, then it is returned.
      * @return the localized language name.
+     * @see Category#DISPLAY
      * @draft ICU 4.4
      * @provisional This API might change or be removed in a future release.
      */
     public String getDisplayLanguageWithDialect() {
-        return getDisplayLanguageInternal(this, getDefault(), true);
+        return getDisplayLanguageInternal(this, getDefault(Category.DISPLAY), true);
     }
 
     /**
@@ -1153,12 +1292,13 @@
     }
 
     /**
-     * {@icu} Returns this locale's script localized for display in the default locale.
+     * {@icu} Returns this locale's script localized for display in the default <code>DISPLAY</code> locale.
      * @return the localized script name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayScript() {
-        return getDisplayScriptInternal(this, getDefault());
+        return getDisplayScriptInternal(this, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1201,12 +1341,13 @@
     }
 
     /**
-     * Returns this locale's country localized for display in the default locale.
+     * Returns this locale's country localized for display in the default <code>DISPLAY</code> locale.
      * @return the localized country name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayCountry() {
-        return getDisplayCountryInternal(this, getDefault());
+        return getDisplayCountryInternal(this, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1250,12 +1391,13 @@
     }
 
     /**
-     * Returns this locale's variant localized for display in the default locale.
+     * Returns this locale's variant localized for display in the default <code>DISPLAY</code> locale.
      * @return the localized variant name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayVariant() {
-        return getDisplayVariantInternal(this, getDefault());
+        return getDisplayVariantInternal(this, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1298,14 +1440,15 @@
     }
 
     /**
-     * {@icu} Returns a keyword localized for display in the default locale.
+     * {@icu} Returns a keyword localized for display in the default <code>DISPLAY</code> locale.
      * @param keyword the keyword to be displayed.
      * @return the localized keyword name.
      * @see #getKeywords()
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public static String getDisplayKeyword(String keyword) {
-        return getDisplayKeywordInternal(keyword, getDefault());
+        return getDisplayKeywordInternal(keyword, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1337,13 +1480,14 @@
     }
 
     /**
-     * {@icu} Returns a keyword value localized for display in the default locale.
+     * {@icu} Returns a keyword value localized for display in the default <code>DISPLAY</code> locale.
      * @param keyword the keyword whose value is to be displayed.
      * @return the localized value name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayKeywordValue(String keyword) {
-        return getDisplayKeywordValueInternal(this, keyword, getDefault());
+        return getDisplayKeywordValueInternal(this, keyword, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1395,12 +1539,13 @@
     }
 
     /**
-     * Returns this locale name localized for display in the default locale.
+     * Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
      * @return the localized locale name.
+     * @see Category#DISPLAY
      * @stable ICU 3.0
      */
     public String getDisplayName() {
-        return getDisplayNameInternal(this, getDefault());
+        return getDisplayNameInternal(this, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1442,14 +1587,15 @@
     }
 
     /**
-     * {@icu} Returns this locale name localized for display in the default locale.
+     * {@icu} Returns this locale name localized for display in the default <code>DISPLAY</code> locale.
      * If a dialect name is present in the locale data, then it is returned.
      * @return the localized locale name.
+     * @see Category#DISPLAY
      * @draft ICU 4.4
      * @provisional This API might change or be removed in a future release.
      */
     public String getDisplayNameWithDialect() {
-        return getDisplayNameWithDialectInternal(this, getDefault());
+        return getDisplayNameWithDialectInternal(this, getDefault(Category.DISPLAY));
     }
 
     /**
@@ -1630,6 +1776,21 @@
                         }
                         return availableLocales[j];
                     }
+                    // compare to scriptless alias, so locales such as
+                    // zh_TW, zh_CN are considered as available locales - see #7190
+                    if (aLocale.getScript().length() == 0
+                            && availableLocales[j].getScript().length() > 0
+                            && availableLocales[j].getLanguage().equals(aLocale.getLanguage())
+                            && availableLocales[j].getCountry().equals(aLocale.getCountry())
+                            && availableLocales[j].getVariant().equals(aLocale.getVariant())) {
+                        ULocale minAvail = ULocale.minimizeSubtags(availableLocales[j]);
+                        if (minAvail.getScript().length() == 0) {
+                            if(setFallback != null) {
+                                setFallback[0] = false; // not a fallback.
+                            }
+                            return aLocale;
+                        }
+                    }
                 }
                 Locale loc = aLocale.toLocale();
                 Locale parent = LocaleUtility.fallback(loc);
@@ -2556,15 +2717,15 @@
 
     /**
      * {@icu} Returns the extension (or private use) value associated with
-     * the specified singleton key, or null if there is no extension
-     * associated with the key.  To be valid, the key must be one
+     * the specified key, or null if there is no extension
+     * associated with the key. To be well-formed, the key must be one
      * of <code>[0-9A-Za-z]</code>.  Keys are case-insensitive, so
      * for example 'z' and 'Z' represent the same extension.
      *
      * @param key the extension key
-     * @return the extension, or null if this locale defines no
-     * extension for the specified key
-     * @throws IllegalArgumentException if the key is not valid
+     * @return The extension, or null if this locale defines no
+     * extension for the specified key.
+     * @throws IllegalArgumentException if key is not well-formed
      * @see #PRIVATE_USE_EXTENSION
      * @see #UNICODE_LOCALE_EXTENSION
      *
@@ -2572,9 +2733,8 @@
      * @provisional This API might change or be removed in a future release.
      */
     public String getExtension(char key) {
-        String strKey = String.valueOf(key);
-        if (!LocaleExtensions.isValidKey(strKey)) {
-            throw new IllegalArgumentException("Invalid extension key: " + strKey);
+        if (!LocaleExtensions.isValidKey(key)) {
+            throw new IllegalArgumentException("Invalid extension key: " + key);
         }
         return extensions().getExtensionValue(key);
     }
@@ -2582,10 +2742,10 @@
     /**
      * {@icu} Returns the set of extension keys associated with this locale, or the
      * empty set if it has no extensions.  The returned set is unmodifiable.
+     * The keys will all be lower-case.
      *
      * @return the set of extension keys, or the empty set if this locale has
      * no extensions
-     *
      * @draft ICU 4.2
      * @provisional This API might change or be removed in a future release.
      */
@@ -2594,32 +2754,47 @@
     }
 
     /**
-     * {@icu} Returns the Unicode locale type associated with the specified Unicode
-     * locale key for this locale.  Unicode locale keywrods are specified
-     * by the 'u' extension and consist of key/type pairs.  The key must be
-     * two alphanumeric characters in length, or an IllegalArgumentException
-     * is thrown.
+     * {@icu} Returns the set of unicode locale attributes associated with
+     * this locale, or the empty set if it has no attributes. The
+     * returned set is unmodifiable.
+     *
+     * @return The set of attributes.
+     * @internal
+     * @deprecated This API is ICU internal only.
+     */
+    public Set<String> getUnicodeLocaleAttributes() {
+        return extensions().getUnicodeLocaleAttributes();
+    }
+
+    /**
+     * {@icu} Returns the Unicode locale type associated with the specified Unicode locale key
+     * for this locale. Returns the empty string for keys that are defined with no type.
+     * Returns null if the key is not defined. Keys are case-insensitive. The key must
+     * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is
+     * thrown.
+     *
      * @param key the Unicode locale key
-     * @return the Unicode locale type associated with the key, or null if the
-     * locale does not define a value for the key.
-     * @throws IllegalArgumentException if the key is not valid.
+     * @return The Unicode locale type associated with the key, or null if the
+     * locale does not define the key.
+     * @throws IllegalArgumentException if the key is not well-formed
+     * @throws NullPointerException if <code>key</code> is null
      *
      * @draft ICU 4.4
      * @provisional This API might change or be removed in a future release.
      */
     public String getUnicodeLocaleType(String key) {
-        if (!LocaleExtensions.isValidKey(key)) {
+        if (!LocaleExtensions.isValidUnicodeLocaleKey(key)) {
             throw new IllegalArgumentException("Invalid Unicode locale key: " + key);
         }
         return extensions().getUnicodeLocaleType(key);
     }
 
     /**
-     * {@icu} Returns the set of keys for Unicode locale keywords defined by this locale,
-     * or null if this locale has no locale extension.  The returned set is
-     * immutable.
+     * {@icu} Returns the set of Unicode locale keys defined by this locale, or the empty set if
+     * this locale has none.  The returned set is immutable.  Keys are all lower case.
      *
-     * @return the set of the Unicode locale keys, or null
+     * @return The set of Unicode locale keys, or the empty set if this locale has
+     * no Unicode locale keywords.
      *
      * @draft ICU 4.4
      * @provisional This API might change or be removed in a future release.
@@ -2632,51 +2807,51 @@
      * {@icu} Returns a well-formed IETF BCP 47 language tag representing
      * this locale.
      *
-     * <p>
-     * If this <code>ULocale</code> object has language, country, or variant
-     * that does not satisfy the IETF BCP 47 language tag syntax requirements,
-     * this method handles these fields as described below:
-     * <p>
-     * <b>Language:</b> If language is empty or ill-formed (for example "a" or "e2"),
-     * it will be emitted as "und" (Undetermined).
-     * <p>
-     * <b>Country:</b> If country is ill-formed (for example "12" or "USA"), it
-     * will be omitted.
-     * <p>
-     * <b>Variant:</b> Variant is treated as consisting of subtags separated by
-     * underscore and converted to lower case letters.  'Well-formed' subtags
-     * consist of either an ASCII letter followed by 4-7 ASCII characters, or an
-     * ASCII digit followed by 3-7 ASCII characters.  If well-formed, the variant
-     * is emitted as each subtag in order (separated by hyphen).  Otherwise:
-     * <ul>
-     * <li>if all sub-segments consist of 1 to 8 ASCII alphanumerics (for example
-     * "WIN", "WINDOWS_XP", "SOLARIS_10"), the first ill-formed variant subtag
-     * and all following sub-segments will be emitted as private use subtags prefixed
-     * by the special private use subtag "variant" followed by each subtag in order
-     * (separated by hyphen).  For example, locale "en_US_WIN" is converted to language
-     * tag "en-US-x-variant-win", locale "de_WINDOWS_XP" is converted to language tag
-     * "de-windows-x-variant-xp".  If this locale has a private use extension value,
-     * the special private use subtags prefixed by "variant" are appended after the
-     * locale's private use value.
-     * <li>if any subtag does not consist of 1 to 8 ASCII alphanumerics, the
-     * variant will be truncated and the problematic subtag and all following
-     * sub-segments will be omitted.  If the remainder is non-empty, it will be
-     * emitted as a private use subtag as above (even if the remainder turns out
-     * to be well-formed).  For example, "Solaris_isjustthecoolestthing" is emitted
-     * as "x-jvariant-Solaris", not as "solaris".</li>
-     * </ul>
+     * <p>If this <code>ULocale</code> has a language, script, country, or
+     * variant that does not satisfy the IETF BCP 47 language tag
+     * syntax requirements, this method handles these fields as
+     * described below:
      *
-     * <p><b>Note:</b> Although the language tag created by this method
-     * satisfies the syntax requirements defined by the IETF BCP 47
-     * specification, it is not always a valid BCP 47 language tag.
-     * For example,
+     * <p><b>Language:</b> If language is empty, or not well-formed
+     * (for example "a" or "e2"), it will be emitted as "und" (Undetermined).
+     *
+     * <p><b>Script:</b> If script is not well-formed (for example "12"
+     * or "Latin"), it will be omitted.
+     * 
+     * <p><b>Country:</b> If country is not well-formed (for example "12"
+     * or "USA"), it will be omitted.
+     *
+     * <p><b>Variant:</b> If variant <b>is</b> well-formed, each sub-segment
+     * (delimited by '-' or '_') is emitted as a subtag.  Otherwise:
+     * <ul>
+     *
+     * <li>if all sub-segments match <code>[0-9a-zA-Z]{1,8}</code>
+     * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first
+     * ill-formed sub-segment and all following will be appended to
+     * the private use subtag.  The first appended subtag will be
+     * "lvariant", followed by the sub-segments in order, separated by
+     * hyphen. For example, "x-lvariant-WIN",
+     * "Oracle-x-lvariant-JDK-Standard-Edition".
+     *
+     * <li>if any sub-segment does not match
+     * <code>[0-9a-zA-Z]{1,8}</code>, the variant will be truncated
+     * and the problematic sub-segment and all following sub-segments
+     * will be omitted.  If the remainder is non-empty, it will be
+     * emitted as a private use subtag as above (even if the remainder
+     * turns out to be well-formed).  For example,
+     * "Solaris_isjustthecoolestthing" is emitted as
+     * "x-lvariant-Solaris", not as "solaris".</li></ul>
+     *
+     * <p><b>Note:</b> Although the language tag created by this
+     * method is well-formed (satisfies the syntax requirements
+     * defined by the IETF BCP 47 specification), it is not
+     * necessarily a valid BCP 47 language tag.  For example,
      * <pre>
-     *   new ULocale("xx_YY").toLanguageTag();
-     * </pre>
-     * will return "xx-YY", but the language subtag "xx" and the region subtag "YY"
-     * are invalid because they are not registered in the
-     * <a href="http://www.iana.org/assignments/language-subtag-registry">
-     * IANA Language Subtag Registry</a>.
+     *   new Locale("xx", "YY").toLanguageTag();</pre>
+     * 
+     * will return "xx-YY", but the language subtag "xx" and the
+     * region subtag "YY" are invalid because they are not registered
+     * in the IANA Language Subtag Registry.
      *
      * @return a BCP47 language tag representing the locale
      * @see #forLanguageTag(String)
@@ -2685,73 +2860,204 @@
      * @provisional This API might change or be removed in a future release.
      */
     public String toLanguageTag() {
-        LanguageTag tag = LanguageTag.parseLocale(base(), extensions());
-        return tag.getID();
+        BaseLocale base = base();
+        LocaleExtensions exts = extensions();
+
+        if (base.getVariant().equalsIgnoreCase("POSIX")) {
+            // special handling for variant POSIX
+            base = BaseLocale.getInstance(base.getLanguage(), base.getScript(), base.getRegion(), "");
+            if (exts.getUnicodeLocaleType("va") == null) {
+                // add va-posix
+                InternalLocaleBuilder ilocbld = new InternalLocaleBuilder();
+                try {
+                    ilocbld.setLocale(BaseLocale.ROOT, exts);
+                    ilocbld.setUnicodeLocaleKeyword("va", "posix");
+                    exts = ilocbld.getLocaleExtensions();
+                } catch (LocaleSyntaxException e) {
+                    // this should not happen
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        LanguageTag tag = LanguageTag.parseLocale(base, exts);
+
+        StringBuilder buf = new StringBuilder();
+        String subtag = tag.getLanguage();
+        if (subtag.length() > 0) {
+            buf.append(LanguageTag.canonicalizeLanguage(subtag));
+        }
+ 
+        subtag = tag.getScript();
+        if (subtag.length() > 0) {
+            buf.append(LanguageTag.SEP);
+            buf.append(LanguageTag.canonicalizeScript(subtag));
+        }
+
+        subtag = tag.getRegion();
+        if (subtag.length() > 0) {
+            buf.append(LanguageTag.SEP);
+            buf.append(LanguageTag.canonicalizeRegion(subtag));
+        }
+
+        List<String>subtags = tag.getVariants();
+        for (String s : subtags) {
+            buf.append(LanguageTag.SEP);
+            buf.append(LanguageTag.canonicalizeVariant(s));
+        }
+
+        subtags = tag.getExtensions();
+        for (String s : subtags) {
+            buf.append(LanguageTag.SEP);
+            buf.append(LanguageTag.canonicalizeExtension(s));
+        }
+
+        subtag = tag.getPrivateuse();
+        if (subtag.length() > 0) {
+            if (buf.length() > 0) {
+                buf.append(LanguageTag.SEP);
+            }
+            buf.append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP);
+            buf.append(LanguageTag.canonicalizePrivateuse(subtag));
+        }
+
+        return buf.toString();
     }
 
     /**
      * {@icu} Returns a locale for the specified IETF BCP 47 language tag string.
-     * If the specified language tag contains any ill-formed subtags,
-     * the first such subtag and all following subtags are ignored.
+     *
+     * <p>If the specified language tag contains any ill-formed subtags,
+     * the first such subtag and all following subtags are ignored.  Compare
+     * to {@link ULocale.Builder#setLanguageTag} which throws an exception
+     * in this case.
+     *
+     * <p>The following <b>conversions</b> are performed:<ul>
+     *
+     * <li>The language code "und" is mapped to language "".
+     *
+     * <li>The portion of a private use subtag prefixed by "lvariant",
+     * if any, is removed and appended to the variant field in the
+     * result locale (without case normalization).  If it is then
+     * empty, the private use subtag is discarded:
+     *
+     * <pre>
+     *     ULocale loc;
+     *     loc = ULocale.forLanguageTag("en-US-x-lvariant-icu4j);
+     *     loc.getVariant(); // returns "ICU4J"
+     *     loc.getExtension('x'); // returns null
+     *
+     *     loc = Locale.forLanguageTag("de-icu4j-x-URP-lvariant-Abc-Def");
+     *     loc.getVariant(); // returns "ICU4J_ABC_DEF"
+     *     loc.getExtension('x'); // returns "urp"
+     * </pre>
+     *
+     * <li>When the languageTag argument contains an extlang subtag,
+     * the first such subtag is used as the language, and the primary
+     * language subtag and other extlang subtags are ignored:
+     *
+     * <pre>
+     *     ULocale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
+     *     ULocale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
+     * </pre>
+     *
+     * <li>Case is normalized. Language is normalized to lower case,
+     * script to title case, country to upper case, variant to upper case,
+     * and extensions to lower case.
      *
      * <p>This implements the 'Language-Tag' production of BCP47, and
      * so supports grandfathered (regular and irregular) as well as
      * private use language tags.  Stand alone private use tags are
      * represented as empty language and extension 'x-whatever',
      * and grandfathered tags are converted to their canonical replacements
-     * where they exist.  Note that a few grandfathered tags have no
-     * modern replacement; these will be converted using the fallback
-     * described above so some information might be lost.
+     * where they exist.  
      *
-     * <p>For a list of grandfathered tags, see the
-     * <a href="http://www.iana.org/assignments/language-subtag-registry">
-     * IANA Language Subtag Registry</a>.
+     * <p>Grandfathered tags with canonical replacements are as follows:
      *
-     * <p><b>Notes:</b> This method converts private use subtags prefixed
-     * by "variant" to variant field in the result locale.  For example,
-     * the code below will return "POSIX".
-     * <pre>
-     *   ULocale.forLanguageTag("en-US-x-variant-posix).getVariant();
-     * </pre>
+     * <table>
+     * <tbody align="center">
+     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>modern replacement</th></tr>
+     * <tr><td>art-lojban</td><td>&nbsp;</td><td>jbo</td></tr>
+     * <tr><td>i-ami</td><td>&nbsp;</td><td>ami</td></tr>
+     * <tr><td>i-bnn</td><td>&nbsp;</td><td>bnn</td></tr>
+     * <tr><td>i-hak</td><td>&nbsp;</td><td>hak</td></tr>
+     * <tr><td>i-klingon</td><td>&nbsp;</td><td>tlh</td></tr>
+     * <tr><td>i-lux</td><td>&nbsp;</td><td>lb</td></tr>
+     * <tr><td>i-navajo</td><td>&nbsp;</td><td>nv</td></tr>
+     * <tr><td>i-pwn</td><td>&nbsp;</td><td>pwn</td></tr>
+     * <tr><td>i-tao</td><td>&nbsp;</td><td>tao</td></tr>
+     * <tr><td>i-tay</td><td>&nbsp;</td><td>tay</td></tr>
+     * <tr><td>i-tsu</td><td>&nbsp;</td><td>tsu</td></tr>
+     * <tr><td>no-bok</td><td>&nbsp;</td><td>nb</td></tr>
+     * <tr><td>no-nyn</td><td>&nbsp;</td><td>nn</td></tr>
+     * <tr><td>sgn-BE-FR</td><td>&nbsp;</td><td>sfb</td></tr>
+     * <tr><td>sgn-BE-NL</td><td>&nbsp;</td><td>vgt</td></tr>
+     * <tr><td>sgn-CH-DE</td><td>&nbsp;</td><td>sgg</td></tr>
+     * <tr><td>zh-guoyu</td><td>&nbsp;</td><td>cmn</td></tr>
+     * <tr><td>zh-hakka</td><td>&nbsp;</td><td>hak</td></tr>
+     * <tr><td>zh-min-nan</td><td>&nbsp;</td><td>nan</td></tr>
+     * <tr><td>zh-xiang</td><td>&nbsp;</td><td>hsn</td></tr>
+     * </tbody>
+     * </table>
+     *
+     * <p>Grandfathered tags with no modern replacement will be
+     * converted as follows:
+     *
+     * <table>
+     * <tbody align="center">
+     * <tr><th>grandfathered tag</th><th>&nbsp;</th><th>converts to</th></tr>
+     * <tr><td>cel-gaulish</td><td>&nbsp;</td><td>xtg-x-cel-gaulish</td></tr>
+     * <tr><td>en-GB-oed</td><td>&nbsp;</td><td>en-GB-x-oed</td></tr>
+     * <tr><td>i-default</td><td>&nbsp;</td><td>en-x-i-default</td></tr>
+     * <tr><td>i-enochian</td><td>&nbsp;</td><td>und-x-i-enochian</td></tr>
+     * <tr><td>i-mingo</td><td>&nbsp;</td><td>see-x-i-mingo</td></tr>
+     * <tr><td>zh-min</td><td>&nbsp;</td><td>nan-x-zh-min</td></tr>
+     * </tbody>
+     * </table>
+     *
+     * <p>For a list of all grandfathered tags, see the
+     * IANA Language Subtag Registry (search for "Type: grandfathered").
+     *
+     * <p><b>Note</b>: there is no guarantee that <code>toLanguageTag</code>
+     * and <code>forLanguageTag</code> will round-trip.
      *
      * @param languageTag the language tag
-     * @return the locale that best represents the language tag
-     * @exception NullPointerException if <code>languageTag</code> is <code>null</code>
+     * @return The locale that best represents the language tag.
+     * @throws NullPointerException if <code>languageTag</code> is <code>null</code>
      * @see #toLanguageTag()
+     * @see ULocale.Builder#setLanguageTag(String)
      *
      * @draft ICU 4.2
      * @provisional This API might change or be removed in a future release.
      */
     public static ULocale forLanguageTag(String languageTag) {
-        LanguageTag tag = LanguageTag.parse(languageTag, true);
-        return getInstance(tag.getBaseLocale(), tag.getLocaleExtensions());
+        LanguageTag tag = LanguageTag.parse(languageTag, null);
+        InternalLocaleBuilder bldr = new InternalLocaleBuilder();
+        bldr.setLanguageTag(tag);
+        return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions());
     }
 
 
     /**
      * <code>Builder</code> is used to build instances of <code>ULocale</code>
-     * from values configured by the setter.  Unlike the <code>ULocale</code>
+     * from values configured by the setters.  Unlike the <code>ULocale</code>
      * constructors, the <code>Builder</code> checks if a value configured by a
-     * setter satisfies the syntactical requirements defined by the <code>ULocale</code>
+     * setter satisfies the syntax requirements defined by the <code>ULocale</code>
      * class.  A <code>ULocale</code> object created by a <code>Builder</code> is
      * well-formed and can be transformed to a well-formed IETF BCP 47 language tag
      * without losing information.
      *
-     * <p>
-     * <b>Note:</b> The <code>ULocale</code> class does not provide
-     * any syntactical restrictions on variant, while BCP 47
-     * requires each variant subtag to be 5 to 8 alphanumeric letters or a single
-     * numeric letter followed by 3 alphanumeric letters.  By default,
-     * the <code>setVariant</code> method throws <code>IllformedLocaleException</code>
-     * for a variant that does not satisfy the syntax above.  If it is
-     * necessary to support such a variant, you could use the constructor <code>
-     * Builder(boolean isLenientVariant)</code> passing <code>true</code> to
-     * skip the syntax validation for variant.  However, you should keep in
-     * mind that a <code>Locale</code> object created this way might lose
-     * the variant information when transformed to a BCP 47 language tag.
+     * <p><b>Note:</b> The <code>ULocale</code> class does not provide any
+     * syntactic restrictions on variant, while BCP 47 requires each variant
+     * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3
+     * alphanumerics.  The method <code>setVariant</code> throws
+     * <code>IllformedLocaleException</code> for a variant that does not satisfy
+     * this restriction. If it is necessary to support such a variant, use a
+     * ULocale constructor.  However, keep in mind that a <code>ULocale</code>
+     * object created this way might lose the variant information when
+     * transformed to a BCP 47 language tag.
      *
-     * <p>
-     * The following example shows how to create a <code>ULocale</code> object
+     * <p>The following example shows how to create a <code>Locale</code> object
      * with the <code>Builder</code>.
      * <blockquote>
      * <pre>
@@ -2780,50 +3086,61 @@
          * @provisional This API might change or be removed in a future release.
          */
         public Builder() {
-            this(false);
+            _locbld = new InternalLocaleBuilder();
         }
 
         /**
          * Constructs an empty Builder with an option whether to allow
          * <code>setVariant</code> to accept a value that does not
          * conform to the IETF BCP 47 variant subtag's syntax requirements.
+         * <p>
+         * <b>Note:</b> This constructor is obsolete and ill-formed variants
+         * are not supported by <code>Builder</code> in ICU4J 4.4.2.2 or later
+         * versions.
          *
          * @param isLenientVariant When true, this <code>Builder</code>
          * will accept an ill-formed variant.
          * @see #setVariant(String)
          *
-         * @draft ICU 4.4
-         * @provisional This API might change or be removed in a future release.
+         * @obsolete ICU 4.4 to be removed
+         * @deprecated This API is obsolete.
          */
         public Builder(boolean isLenientVariant) {
-            _locbld = new InternalLocaleBuilder(isLenientVariant);
+            _locbld = new InternalLocaleBuilder();
         }
 
         /**
          * Returns true if this <code>Builder</code> accepts a value that does
          * not conform to the IETF BCP 47 variant subtag's syntax requirements
          * in <code>setVariant</code>
+         * <p>
+         * <b>Note:</b> This method is obsolete and always returns false in ICU4J
+         * 4.4.2.2 or later versions.
          *
          * @return true if this <code>Build</code> accepts an ill-formed variant.
          *
-         * @draft ICU 4.4
-         * @provisional This API might change or be removed in a future release.
+         * @obsolete ICU 4.4 to be removed
+         * @deprecated This API is obsolete.
          */
         public boolean isLenientVariant() {
-            return _locbld.isLenientVariant();
+            return false;
         }
 
 
         /**
-         * Resets the <code>Builder</code> to match the provided <code>locale</code>.
-         * The previous state of the builder is discarded.  Fields that do
-         * not conform to the <code>ULocale</code> class specification, for example,
-         * a single letter language, are ill-formed.
+         * Resets the <code>Builder</code> to match the provided
+         * <code>locale</code>.  Existing state is discarded.
+         *
+         * <p>All fields of the locale must be well-formed, see {@link Locale}.
+         *
+         * <p>Locales with any ill-formed fields cause
+         * <code>IllformedLocaleException</code> to be thrown.
          *
          * @param locale the locale
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>locale</code> has
          * any ill-formed fields.
+         * @throws NullPointerException if <code>locale</code> is null.
          *
          * @draft ICU 4.2
          * @provisional This API might change or be removed in a future release.
@@ -2838,48 +3155,47 @@
         }
 
         /**
-         * Resets the builder to match the provided IETF BCP 47 language tag.
-         * The previous state of the builder is discarded.
+         * Resets the Builder to match the provided IETF BCP 47
+         * language tag.  Discards the existing state.  Null and the
+         * empty string cause the builder to be reset, like {@link
+         * #clear}.  Grandfathered tags (see {@link
+         * ULocale#forLanguageTag}) are converted to their canonical
+         * form before being processed.  Otherwise, the language tag
+         * must be well-formed (see {@link ULocale}) or an exception is
+         * thrown (unlike <code>ULocale.forLanguageTag</code>, which
+         * just discards ill-formed and following portions of the
+         * tag).
          *
          * @param languageTag the language tag
-         * @return this builder
-         * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed.
-         * @throws NullPointerException if <code>languageTag</code> is null.
+         * @return This builder.
+         * @throws IllformedLocaleException if <code>languageTag</code> is ill-formed
          * @see ULocale#forLanguageTag(String)
          *
          * @draft ICU 4.2
          * @provisional This API might change or be removed in a future release.
          */
         public Builder setLanguageTag(String languageTag) {
-            LanguageTag tag = null;
-            try {
-                tag = LanguageTag.parseStrict(languageTag, _locbld.isLenientVariant());
-            } catch (LocaleSyntaxException e) {
-                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
+            ParseStatus sts = new ParseStatus();
+            LanguageTag tag = LanguageTag.parse(languageTag, sts);
+            if (sts.isError()) {
+                throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex());
             }
-
-            try {
-                _locbld.setLocale(tag.getBaseLocale(),tag.getLocaleExtensions());
-            } catch (LocaleSyntaxException e) {
-                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
-            }
+            _locbld.setLanguageTag(tag);
 
             return this;
         }
 
         /**
-         * Sets the language.  If <code>language</code> is the empty string,
-         * the language in this <code>Builder</code> will be removed.
-         * Typical language value is a two or three-letter language
+         * Sets the language.  If <code>language</code> is the empty string or
+         * null, the language in this <code>Builder</code> is removed.  Otherwise,
+         * the language must be <a href="./Locale.html#def_language">well-formed</a>
+         * or an exception is thrown.
+         *
+         * <p>The typical language value is a two or three-letter language
          * code as defined in ISO639.
-         * Well-formed values are any string of two to eight alpha
-         * letters.  This method accepts upper case alpha letters
-         * [A-Z], but the language value in the <code>ULocale</code>
-         * created by the <code>Builder</code> is always normalized
-         * to lower case letters.
          *
          * @param language the language
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>language</code> is ill-formed
          *
          * @draft ICU 4.2
@@ -2895,17 +3211,14 @@
         }
 
         /**
-         * Sets the script.  If <code>script</code> is the empty string,
+         * Sets the script. If <code>script</code> is null or the empty string,
          * the script in this <code>Builder</code> is removed.
-         * Typical script value is a four-letter script code as defined by ISO 15924.
-         * Well-formed values are any string of four alpha letters.
-         * This method accepts both upper and lower case alpha letters [a-zA-Z],
-         * but the script value in the <code>ULocale</code> created by the
-         * <code>Builder</code> is always normalized to title case
-         * (the first letter is upper case and the rest of letters are lower case).
+         * Otherwise, the script must be well-formed or an exception is thrown.
+         *
+         * <p>The typical script value is a four-letter script code as defined by ISO 15924.
          *
          * @param script the script
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>script</code> is ill-formed
          *
          * @draft ICU 4.2
@@ -2921,16 +3234,18 @@
         }
 
         /**
-         * Sets the region.  If region is the empty string, the region
-         * in this <code>Builder</code> is removed.
-         * Typical region value is a two-letter ISO 3166 code or a three-digit UN M.49
-         * area code.  Well-formed values are any two-letter or three-digit string.
-         * This method accepts lower case letters [a-z], but the country value in
-         * the <code>ULocale</code> created by the <code>Builder</code> is always
-         * normalized to upper case.
+         * Sets the region.  If region is null or the empty string, the region
+         * in this <code>Builder</code> is removed.  Otherwise,
+         * the region must be well-formed or an exception is thrown.
+         *
+         * <p>The typical region value is a two-letter ISO 3166 code or a
+         * three-digit UN M.49 area code.
+         *
+         * <p>The country value in the <code>Locale</code> created by the
+         * <code>Builder</code> is always normalized to upper case.
          *
          * @param region the region
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>region</code> is ill-formed
          *
          * @draft ICU 4.2
@@ -2946,18 +3261,19 @@
         }
 
         /**
-         * Sets the variant.  If variant is the empty string, the
-         * variant in this <code>Builder</code> is removed.
-         * <p>
-         * <b>Note:</b> By default, this method checks if <code>variant</code>
-         * satisfies the IETF BCP 47 variant subtag's syntax requirements.
-         * However, the <code>ULocale</code> class itself does not impose any syntactical
-         * restriction on variant.  When a <code>Builder</code> is created by the
-         * constructor <code>Builder(boolean isLenientVariant)</code>
-         * with <code>true</code>, this method skips the syntax check.
+         * Sets the variant.  If variant is null or the empty string, the
+         * variant in this <code>Builder</code> is removed.  Otherwise, it
+         * must consist of one or more well-formed subtags, or an exception is thrown.
+         *
+         * <p><b>Note:</b> This method checks if <code>variant</code>
+         * satisfies the IETF BCP 47 variant subtag's syntax requirements,
+         * and normalizes the value to lowercase letters.  However,
+         * the <code>ULocale</code> class does not impose any syntactic
+         * restriction on variant.  To set such a variant,
+         * use a ULocale constructor.
          *
          * @param variant the variant
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>variant</code> is ill-formed
          *
          * @draft ICU 4.2
@@ -2973,32 +3289,23 @@
         }
 
         /**
-         * Sets the extension for the given key. If the value is the
-         * empty string, the extension is removed. Legal keys are
-         * characters in the ranges <code>[0-9A-Za-z]</code>.  Keys
-         * are case-insensitive, so for example 'z' and 'Z' represent
-         * the same extension. In general, well-formed values are any
-         * series of fields of two to eight alphanumeric characters,
-         * separated by hyphen or underscore.
+         * Sets the extension for the given key. If the value is null or the
+         * empty string, the extension is removed.  Otherwise, the extension
+         * must be well-formed or an exception is thrown.
          *
          * <p><b>Note:</b> The key {@link ULocale#UNICODE_LOCALE_EXTENSION
          * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension.
          * Setting a value for this key replaces any existing Unicode locale key/type
          * pairs with those defined in the extension.
-         * To be well-formed, a value for this extension must meet the additional
-         * constraints that each locale key is two alphanumeric characters,
-         * followed by at least one locale type subtag represented by
-         * three to eight alphanumeric characters, and that the keys and types
-         * be legal Unicode locale keys and values.
          *
          * <p><b>Note:</b> The key {@link ULocale#PRIVATE_USE_EXTENSION
          * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be
-         * well-formed, the value for this key needs only to have fields of one to
+         * well-formed, the value for this key needs only to have subtags of one to
          * eight alphanumeric characters, not two to eight as in the general case.
          *
          * @param key the extension key
          * @param value the extension value
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>key</code> is illegal
          * or <code>value</code> is ill-formed
          * @see #setUnicodeLocaleKeyword(String, String)
@@ -3016,19 +3323,23 @@
         }
 
         /**
-         * Sets the Unicode locale keyword type for the given key.  If the
-         * value is the empty string, the Unicode keyword is removed.
-         * Well-formed keys are strings of two alphanumeric characters.
-         * Well-formed types are one or more subtags where each of them is
-         * three to eight alphanumeric characters.
-         * <p>
-         * <b>Note</b>:Setting the 'u' extension replaces all Unicode locale
-         * keywords with those defined in the extension.
+         * Sets the Unicode locale keyword type for the given key.  If the type
+         * is null, the Unicode keyword is removed.  Otherwise, the key must be
+         * non-null and both key and type must be well-formed or an exception
+         * is thrown.
+         *
+         * <p>Keys and types are converted to lower case.
+         *
+         * <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
+         * replaces all Unicode locale keywords with those defined in the
+         * extension.
+         *
          * @param key the Unicode locale key
          * @param type the Unicode locale type
-         * @return this builder
+         * @return This builder.
          * @throws IllformedLocaleException if <code>key</code> or <code>type</code>
          * is ill-formed
+         * @throws NullPointerException if <code>key</code> is null
          * @see #setExtension(char, String)
          *
          * @draft ICU 4.4
@@ -3036,7 +3347,55 @@
          */
         public Builder setUnicodeLocaleKeyword(String key, String type) {
             try {
-                _locbld.setUnicodeLocaleExtension(key, type);
+                _locbld.setUnicodeLocaleKeyword(key, type);
+            } catch (LocaleSyntaxException e) {
+                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
+            }
+            return this;
+        }
+
+        /**
+         * Adds a unicode locale attribute, if not already present, otherwise
+         * has no effect.  The attribute must not be null and must be well-formed
+         * or an exception is thrown.
+         *
+         * @param attribute the attribute
+         * @return This builder.
+         * @throws NullPointerException if <code>attribute</code> is null
+         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
+         * @see #setExtension(char, String)
+         *
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        public Builder addUnicodeLocaleAttribute(String attribute) {
+            try {
+                _locbld.addUnicodeLocaleAttribute(attribute);
+            } catch (LocaleSyntaxException e) {
+                throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
+            }
+            return this;
+        }
+
+        /**
+         * Removes a unicode locale attribute, if present, otherwise has no
+         * effect.  The attribute must not be null and must be well-formed
+         * or an exception is thrown.
+         *
+         * <p>Attribute comparision for removal is case-insensitive.
+         *
+         * @param attribute the attribute
+         * @return This builder.
+         * @throws NullPointerException if <code>attribute</code> is null
+         * @throws IllformedLocaleException if <code>attribute</code> is ill-formed
+         * @see #setExtension(char, String)
+         *
+         * @internal
+         * @deprecated This API is ICU internal only.
+         */
+        public Builder removeUnicodeLocaleAttribute(String attribute) {
+            try {
+                _locbld.removeUnicodeLocaleAttribute(attribute);
             } catch (LocaleSyntaxException e) {
                 throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex());
             }
@@ -3067,12 +3426,12 @@
          * @provisional This API might change or be removed in a future release.
          */
         public Builder clearExtensions() {
-            _locbld.removeLocaleExtensions();
+            _locbld.clearExtensions();
             return this;
         }
 
         /**
-         * Returns an instance of Locale created from the fields set
+         * Returns an instance of <code>ULocale</code> created from the fields set
          * on this builder.
          *
          * @return a new Locale
@@ -3100,14 +3459,31 @@
                 Extension ext = exts.getExtension(key);
                 if (ext instanceof UnicodeLocaleExtension) {
                     UnicodeLocaleExtension uext = (UnicodeLocaleExtension)ext;
-                    Set<String> ukeys = uext.getKeys();
+                    Set<String> ukeys = uext.getUnicodeLocaleKeys();
                     for (String bcpKey : ukeys) {
-                        String bcpType = uext.getType(bcpKey);
+                        String bcpType = uext.getUnicodeLocaleType(bcpKey);
                         // convert to legacy key/type
                         String lkey = bcp47ToLDMLKey(bcpKey);
-                        String ltype = bcp47ToLDMLType(lkey, bcpType);
+                        String ltype = bcp47ToLDMLType(lkey, ((bcpType.length() == 0) ? "true" : bcpType)); // use "true" as the value of typeless keywords
+                        // special handling for u-va-posix, since this is a variant, not a keyword
+                        if (lkey.equals("va") && ltype.equals("posix") && base.getVariant().length() == 0) {
+                            id = id + "_POSIX";
+                        } else {
                         kwds.put(lkey, ltype);
                     }
+                    }
+                    // Mapping Unicode locale attribute to the special keyword, attribute=xxx-yyy
+                    Set<String> uattributes = uext.getUnicodeLocaleAttributes();
+                    if (uattributes.size() > 0) {
+                        StringBuilder attrbuf = new StringBuilder();
+                        for (String attr : uattributes) {
+                            if (attrbuf.length() > 0) {
+                                attrbuf.append('-');
+                            }
+                            attrbuf.append(attr);
+                        }
+                        kwds.put(LOCALE_ATTRIBUTE_KEY, attrbuf.toString());
+                    }
                 } else {
                     kwds.put(String.valueOf(key), ext.getValue());
                 }
@@ -3155,12 +3531,22 @@
                 InternalLocaleBuilder intbld = new InternalLocaleBuilder();
                 while (kwitr.hasNext()) {
                     String key = kwitr.next();
-                    if (key.length() >= 2) {
+                    if (key.equals(LOCALE_ATTRIBUTE_KEY)) {
+                        // special keyword used for representing Unicode locale attributes
+                        String[] uattributes = getKeywordValue(key).split("[-_]");
+                        for (String uattr : uattributes) {
+                            try {
+                                intbld.addUnicodeLocaleAttribute(uattr);
+                            } catch (LocaleSyntaxException e) {
+                                // ignore and fall through
+                            }
+                        }
+                    } else if (key.length() >= 2) {
                         String bcpKey = ldmlKeyToBCP47(key);
                         String bcpType = ldmlTypeToBCP47(key, getKeywordValue(key));
                         if (bcpKey != null && bcpType != null) {
                             try {
-                                intbld.setUnicodeLocaleExtension(bcpKey, bcpType);
+                                intbld.setUnicodeLocaleKeyword(bcpKey, bcpType);
                             } catch (LocaleSyntaxException e) {
                                 // ignore and fall through
                             }
@@ -3311,4 +3697,376 @@
         }
         return type;
     }
+
+    /*
+     * JDK Locale Helper
+     */
+    private static final class JDKLocaleHelper {
+        private static boolean isJava7orNewer = false;
+
+        /*
+         * New methods in Java 7 Locale class
+         */
+        private static Method mGetScript;
+        private static Method mGetExtensionKeys;
+        private static Method mGetExtension;
+        private static Method mGetUnicodeLocaleKeys;
+        private static Method mGetUnicodeLocaleAttributes;
+        private static Method mGetUnicodeLocaleType;
+        private static Method mForLanguageTag;
+
+        private static Method mGetDefault;
+        private static Method mSetDefault;
+        private static Object eDISPLAY;
+        private static Object eFORMAT;
+
+        /*
+         * This table is used for mapping between ICU and special Java
+         * 6 locales.  When an ICU locale matches <minumum base> with
+         * <keyword>/<value>, the ICU locale is mapped to <Java> locale.
+         * For example, both ja_JP@calendar=japanese and ja@calendar=japanese
+         * are mapped to Java locale "ja_JP_JP".  ICU locale "nn" is mapped
+         * to Java locale "no_NO_NY".
+         */
+        private static final String[][] JAVA6_MAPDATA = {
+        //  { <Java>,       <ICU base>, <keyword>,  <value>,    <minimum base>
+            { "ja_JP_JP",   "ja_JP",    "calendar", "japanese", "ja"},
+            { "no_NO_NY",   "nn_NO",    null,       null,       "nn"},
+            { "th_TH_TH",   "th_TH",    "numbers",  "thai",     "th"},
+        };
+
+        static {
+            do {
+                try {
+                    mGetScript = Locale.class.getMethod("getScript", (Class[]) null);
+                    mGetExtensionKeys = Locale.class.getMethod("getExtensionKeys", (Class[]) null);
+                    mGetExtension = Locale.class.getMethod("getExtension", char.class);
+                    mGetUnicodeLocaleKeys = Locale.class.getMethod("getUnicodeLocaleKeys", (Class[]) null);
+                    mGetUnicodeLocaleAttributes = Locale.class.getMethod("getUnicodeLocaleAttributes", (Class[]) null);
+                    mGetUnicodeLocaleType = Locale.class.getMethod("getUnicodeLocaleType", String.class);
+                    mForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
+    
+                    Class<?> cCategory = null;
+                    Class<?>[] classes = Locale.class.getDeclaredClasses();
+                    for (Class<?> c : classes) {
+                        if (c.getName().equals("java.util.Locale$Category")) {
+                            cCategory = c;
+                            break;
+                        }
+                    }
+                    if (cCategory == null) {
+                        break;
+                    }
+                    mGetDefault = Locale.class.getDeclaredMethod("getDefault", cCategory);
+                    mSetDefault = Locale.class.getDeclaredMethod("setDefault", cCategory, Locale.class);
+    
+                    Method mName = cCategory.getMethod("name", (Class[]) null);
+                    Object[] enumConstants = cCategory.getEnumConstants();
+                    for (Object e : enumConstants) {
+                        String catVal = (String)mName.invoke(e, (Object[])null);
+                        if (catVal.equals("DISPLAY")) {
+                            eDISPLAY = e;
+                        } else if (catVal.equals("FORMAT")) {
+                            eFORMAT = e;
+                        }
+                    }
+                    if (eDISPLAY == null || eFORMAT == null) {
+                        break;
+                    }    
+                    isJava7orNewer = true;
+                } catch (NoSuchMethodException e) {
+                } catch (IllegalArgumentException e) {
+                } catch (IllegalAccessException e) {
+                } catch (InvocationTargetException e) {
+                } catch (SecurityException e) {
+                    // TODO : report?
+                }
+            } while (false);
+        }
+
+        private JDKLocaleHelper() {
+        }
+
+        public static boolean isJava7orNewer() {
+            return isJava7orNewer;
+        }
+
+        public static ULocale toULocale(Locale loc) {
+            return isJava7orNewer ? toULocale7(loc) : toULocale6(loc);
+        }
+
+        public static Locale toLocale(ULocale uloc) {
+            return isJava7orNewer ? toLocale7(uloc) : toLocale6(uloc);
+        }
+
+        private static ULocale toULocale7(Locale loc) {
+            String language = loc.getLanguage();
+            String script = "";
+            String country = loc.getCountry();
+            String variant = loc.getVariant();
+
+            Set<String> attributes = null;
+            Map<String, String> keywords = null;
+
+            try {
+                script = (String) mGetScript.invoke(loc, (Object[]) null);
+                @SuppressWarnings("unchecked")
+                Set<Character> extKeys = (Set<Character>) mGetExtensionKeys.invoke(loc, (Object[]) null);
+                if (!extKeys.isEmpty()) {
+                    for (Character extKey : extKeys) {
+                        if (extKey.charValue() == 'u') {
+                            // Found Unicode locale extension
+
+                            // attributes
+                            @SuppressWarnings("unchecked")
+                            Set<String> uAttributes = (Set<String>) mGetUnicodeLocaleAttributes.invoke(loc, (Object[]) null);
+                            if (!uAttributes.isEmpty()) {
+                                attributes = new TreeSet<String>();
+                                for (String attr : uAttributes) {
+                                    attributes.add(attr);
+                                }
+                            }
+
+                            // keywords
+                            @SuppressWarnings("unchecked")
+                            Set<String> uKeys = (Set<String>) mGetUnicodeLocaleKeys.invoke(loc, (Object[]) null);
+                            for (String kwKey : uKeys) {
+                                String kwVal = (String) mGetUnicodeLocaleType.invoke(loc, kwKey);
+                                if (kwVal != null) {
+                                    if (kwKey.equals("va")) {
+                                        // va-* is interpreted as a variant
+                                        variant = (variant.length() == 0) ? kwVal : kwVal + "_" + variant;
+                                    } else {
+                                        if (keywords == null) {
+                                            keywords = new TreeMap<String, String>();
+                                        }
+                                        keywords.put(kwKey, kwVal);
+                                    }
+                                }
+                            }
+                        } else {
+                            String extVal = (String) mGetExtension.invoke(loc, extKey);
+                            if (extVal != null) {
+                                if (keywords == null) {
+                                    keywords = new TreeMap<String, String>();
+                                }
+                                keywords.put(String.valueOf(extKey), extVal);
+                            }
+                        }
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e);
+            }
+
+            // JDK locale no_NO_NY is not interpreted as Nynorsk by ICU,
+            // and it should be transformed to nn_NO.
+
+            // Note: JDK7+ unerstand both no_NO_NY and nn_NO. When convert
+            // ICU locale to JDK, we do not need to map nn_NO back to no_NO_NY.
+
+            if (language.equals("no") && country.equals("NO") && variant.equals("NY")) {
+                language = "nn";
+                variant = "";
+            }
+
+            // Constructing ID
+            StringBuilder buf = new StringBuilder(language);
+
+            if (script.length() > 0) {
+                buf.append('_');
+                buf.append(script);
+            }
+
+            if (country.length() > 0) {
+                buf.append('_');
+                buf.append(country);
+            }
+
+            if (variant.length() > 0) {
+                if (country.length() == 0) {
+                    buf.append('_');
+                }
+                buf.append('_');
+                buf.append(variant);
+            }
+
+            if (attributes != null) {
+                // transform Unicode attributes into a keyword
+                StringBuilder attrBuf = new StringBuilder();
+                for (String attr : attributes) {
+                    if (attrBuf.length() != 0) {
+                        attrBuf.append('-');
+                    }
+                    attrBuf.append(attr);
+                }
+                if (keywords == null) {
+                    keywords = new TreeMap<String, String>();
+                }
+                keywords.put(LOCALE_ATTRIBUTE_KEY, attrBuf.toString());
+            }
+
+            if (keywords != null) {
+                buf.append('@');
+                boolean addSep = false;
+                for (Entry<String, String> kwEntry : keywords.entrySet()) {
+                    String kwKey = kwEntry.getKey();
+                    String kwVal = kwEntry.getValue();
+
+                    if (kwKey.length() != 1) {
+                        // Unicode locale key
+                        kwKey = bcp47ToLDMLKey(kwKey);
+                        // use "true" as the value of typeless keywords
+                        kwVal = bcp47ToLDMLType(kwKey, ((kwVal.length() == 0) ? "true" : kwVal));
+                    }
+
+                    if (addSep) {
+                        buf.append(';');
+                    } else {
+                        addSep = true;
+                    }
+                    buf.append(kwKey);
+                    buf.append('=');
+                    buf.append(kwVal);
+                }
+            }
+
+            return new ULocale(getName(buf.toString()), loc);
+        }
+
+        private static ULocale toULocale6(Locale loc) {
+            ULocale uloc = null;
+            String locStr = loc.toString();
+            if (locStr.length() == 0) {
+                uloc = ULocale.ROOT;
+            } else {
+                for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
+                    if (JAVA6_MAPDATA[i][0].equals(locStr)) {
+                        LocaleIDParser p = new LocaleIDParser(JAVA6_MAPDATA[i][1]);
+                        p.setKeywordValue(JAVA6_MAPDATA[i][2], JAVA6_MAPDATA[i][3]);
+                        locStr = p.getName();
+                        break;
+                    }
+                }
+                uloc = new ULocale(getName(locStr), loc);
+            }
+            return uloc;
+        }
+
+        private static Locale toLocale7(ULocale uloc) {
+            Locale loc = null;
+            String ulocStr = uloc.getName();
+            if (uloc.getScript().length() > 0 || ulocStr.contains("@")) {
+                // With script or keywords available, the best way
+                // to get a mapped Locale is to go through a language tag.
+                // A Locale with script or keywords can only have variants
+                // that is 1 to 8 alphanum. If this ULocale has a variant
+                // subtag not satisfying the criteria, the variant subtag
+                // will be lost.
+                String tag = uloc.toLanguageTag();
+
+                // Workaround for variant casing problem:
+                //
+                // The variant field in ICU is case insensitive and normalized
+                // to upper case letters by getVariant(), while
+                // the variant field in JDK Locale is case sensitive.
+                // ULocale#toLanguageTag use lower case characters for
+                // BCP 47 variant and private use x-lvariant.
+                //
+                // Locale#forLanguageTag in JDK preserves character casing
+                // for variant. Because ICU always normalizes variant to
+                // upper case, we convert language tag to upper case here.
+                tag = AsciiUtil.toUpperString(tag);
+
+                try {
+                    loc = (Locale)mForLanguageTag.invoke(null, tag);
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException(e);
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            if (loc == null) {
+                // Without script or keywords, use a Locale constructor,
+                // so we can preserve any ill-formed variants.
+                loc = new Locale(uloc.getLanguage(), uloc.getCountry(), uloc.getVariant());
+            }
+            return loc;
+        }
+
+        private static Locale toLocale6(ULocale uloc) {
+            String locstr = uloc.getBaseName();
+            for (int i = 0; i < JAVA6_MAPDATA.length; i++) {
+                if (locstr.equals(JAVA6_MAPDATA[i][1]) || locstr.equals(JAVA6_MAPDATA[i][4])) {
+                    if (JAVA6_MAPDATA[i][2] != null) {
+                        String val = uloc.getKeywordValue(JAVA6_MAPDATA[i][2]);
+                        if (val != null && val.equals(JAVA6_MAPDATA[i][3])) {
+                            locstr = JAVA6_MAPDATA[i][0];
+                            break;
+                        }
+                    } else {
+                        locstr = JAVA6_MAPDATA[i][0];
+                        break;
+                    }
+                }
+            }
+            LocaleIDParser p = new LocaleIDParser(locstr);
+            String[] names = p.getLanguageScriptCountryVariant();
+            return new Locale(names[0], names[2], names[3]);
+        }
+
+        public static Locale getDefault(Category category) {
+            Locale loc = Locale.getDefault();
+            if (isJava7orNewer) {
+                Object cat = null;
+                switch (category) {
+                case DISPLAY:
+                    cat = eDISPLAY;
+                    break;
+                case FORMAT:
+                    cat = eFORMAT;
+                    break;
+                }
+                if (cat != null) {
+                    try {
+                        loc = (Locale)mGetDefault.invoke(null, cat);
+                    } catch (InvocationTargetException e) {
+                        // fall through - use the base default
+                    } catch (IllegalArgumentException e) {
+                        // fall through - use the base default
+                    } catch (IllegalAccessException e) {
+                        // fall through - use the base default
+                    }
+                }
+            }
+            return loc;
+        }
+
+        public static void setDefault(Category category, Locale newLocale) {
+            if (isJava7orNewer) {
+                Object cat = null;
+                switch (category) {
+                case DISPLAY:
+                    cat = eDISPLAY;
+                    break;
+                case FORMAT:
+                    cat = eFORMAT;
+                    break;
+                }
+                if (cat != null) {
+                    try {
+                        mSetDefault.invoke(null, cat, newLocale);
+                    } catch (InvocationTargetException e) {
+                        // fall through - no effects
+                    } catch (IllegalArgumentException e) {
+                        // fall through - no effects
+                    } catch (IllegalAccessException e) {
+                        // fall through - no effects
+                    }
+                }
+            }
+        }
+    }
 }
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 681fa84..4d6c3d7 100644
--- a/main/classes/core/src/com/ibm/icu/util/VersionInfo.java
+++ b/main/classes/core/src/com/ibm/icu/util/VersionInfo.java
@@ -469,7 +469,7 @@
         UNICODE_5_0   = getInstance(5, 0, 0, 0);
         UNICODE_5_1   = getInstance(5, 1, 0, 0);
         UNICODE_5_2   = getInstance(5, 2, 0, 0);
-        ICU_VERSION   = getInstance(4, 4, 2, 1);
+        ICU_VERSION   = getInstance(4, 4, 2, 2);
         ICU_DATA_VERSION = getInstance(4, 4, 0, 1);
         UCOL_RUNTIME_VERSION = getInstance(6);
         UCOL_BUILDER_VERSION = getInstance(7);
diff --git a/main/classes/translit/src/com/ibm/icu/text/Transliterator.java b/main/classes/translit/src/com/ibm/icu/text/Transliterator.java
index 1356863..d1aa5c2 100644
--- a/main/classes/translit/src/com/ibm/icu/text/Transliterator.java
+++ b/main/classes/translit/src/com/ibm/icu/text/Transliterator.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.                                                *
  *******************************************************************************
  */
@@ -20,6 +20,7 @@
 import com.ibm.icu.text.TransliteratorIDParser.SingleID;
 import com.ibm.icu.util.CaseInsensitiveString;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Category;
 import com.ibm.icu.util.UResourceBundle;
 
 /**
@@ -1176,12 +1177,13 @@
 
     /**
      * Returns a name for this transliterator that is appropriate for
-     * display to the user in the default locale.  See {@link
+     * display to the user in the default <code>DISPLAY</code> locale.  See {@link
      * #getDisplayName(String,Locale)} for details.
+     * @see com.ibm.icu.util.ULocale.Category#DISPLAY
      * @stable ICU 2.0
      */
     public final static String getDisplayName(String ID) {
-        return getDisplayName(ID, ULocale.getDefault());
+        return getDisplayName(ID, ULocale.getDefault(Category.DISPLAY));
     }
 
     /**
diff --git a/main/shared/build/common.properties b/main/shared/build/common.properties
index 401da32..46e293f 100644
--- a/main/shared/build/common.properties
+++ b/main/shared/build/common.properties
@@ -5,7 +5,7 @@
 
 # Version numbers, etc.
 icu4j.spec.version = 4.4
-icu4j.impl.version = 4.4.2.1
+icu4j.impl.version = 4.4.2.2
 icu4j.data.version = 44
 current.year = 2011
 
diff --git a/main/shared/data/icudata.jar b/main/shared/data/icudata.jar
index c277940..4c3f870 100755
--- a/main/shared/data/icudata.jar
+++ b/main/shared/data/icudata.jar
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f359ce0fb10ed8c889afd8464e74bde8feac5c08dffae230368e2142ce726b91
-size 6990828
+oid sha256:c35d58240ba0d7a92fce6316791c1808950d0a0ec01bc120174ae29aab9dace8
+size 6991911
diff --git a/main/shared/licenses/license.html b/main/shared/licenses/license.html
index 7ae0252..1e2b1ab 100644
--- a/main/shared/licenses/license.html
+++ b/main/shared/licenses/license.html
@@ -11,7 +11,7 @@
 <p>COPYRIGHT AND PERMISSION NOTICE</p>
 
 <p>
-Copyright (c) 1995-2010 International Business Machines Corporation and others
+Copyright (c) 1995-2011 International Business Machines Corporation and others
 </p>
 <p>
 All rights reserved.
diff --git a/main/tests/charset/src/com/ibm/icu/dev/test/charset/TestCharset.java b/main/tests/charset/src/com/ibm/icu/dev/test/charset/TestCharset.java
index 68c9d4c..2da81ad 100644
--- a/main/tests/charset/src/com/ibm/icu/dev/test/charset/TestCharset.java
+++ b/main/tests/charset/src/com/ibm/icu/dev/test/charset/TestCharset.java
@@ -1,6 +1,6 @@
 /**
 *******************************************************************************
-* Copyright (C) 2006-2010, International Business Machines Corporation and    *
+* Copyright (C) 2006-2011, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 *
@@ -2787,6 +2787,27 @@
         smBufDecode(decoder, "UTF-7", bs, us);
         smBufEncode(encoder, "UTF-7", us, bs);
         
+        /* Testing UTF-7 toUnicode with substitute callbacks */
+        {
+            byte [] bytesTestErrorConsumption = {
+                    /* a~       a+AB~                         a+AB\x0c                      a+AB-                         a+AB.                         a+. */
+                    0x61, 0x7e, 0x61, 0x2b, 0x41, 0x42, 0x7e, 0x61, 0x2b, 0x41, 0x42, 0x0c, 0x61, 0x2b, 0x41, 0x42, 0x2d, 0x61, 0x2b, 0x41, 0x42, 0x2e, 0x61, 0x2b, 0x2e
+    
+            };
+            char [] unicodeTestErrorConsumption = {
+                    0x61, 0xfffd, 0x61, 0xfffd, 0xfffd, 0x61, 0xfffd, 0xfffd, 0x61, 0xfffd, 0x61, 0xfffd, 0x2e, 0x61, 0xfffd, 0x2e
+            };
+            bs = ByteBuffer.wrap(bytesTestErrorConsumption);
+            us = CharBuffer.wrap(unicodeTestErrorConsumption);
+    
+            CodingErrorAction savedMal = decoder.malformedInputAction();
+            CodingErrorAction savedUMap = decoder.unmappableCharacterAction();
+            decoder.onMalformedInput(CodingErrorAction.REPLACE);
+            decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+            smBufDecode(decoder, "UTF-7 DE Error Consumption", bs, us);
+            decoder.onMalformedInput(savedMal);
+            decoder.onUnmappableCharacter(savedUMap);
+        }
         /* ticket 6151 */
         CharBuffer smallus = CharBuffer.allocate(1);
         ByteBuffer bigbs = ByteBuffer.allocate(3);
@@ -3139,7 +3160,7 @@
         
         //test for overflow buffer error
         ccus.put((char)0x2262);
-        ccbs.put((byte)0x2b); ccbs.put((byte)0x49); ccbs.put((byte)0x6d); ccbs.put((byte)0x49);
+        ccbs.put((byte)0x2b); ccbs.put((byte)0x49); ccbs.put((byte)0x6d); ccbs.put((byte)0x49); ccbs.put((byte)0x2d);
         
         ccbs.limit(ccbs.position());
         ccbs.position(0);
@@ -3157,7 +3178,7 @@
         //test for overflow buffer error
         encoder.reset();
         ccus.put((char)0x3980); ccus.put((char)0x2715);
-        ccbs.put((byte)0x2b); ccbs.put((byte)0x4f); ccbs.put((byte)0x59);
+        ccbs.put((byte)0x2b); ccbs.put((byte)0x4f); ccbs.put((byte)0x59); ccbs.put((byte)0x2d);
         
         ccbs.limit(ccbs.position());
         ccbs.position(0);
diff --git a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationAPITest.java b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationAPITest.java
index a3571ee..0cde874 100644
--- a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationAPITest.java
+++ b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationAPITest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2002-2009, International Business Machines Corporation and         *
+ * Copyright (C) 2002-2011, International Business Machines Corporation and         *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -24,11 +24,11 @@
 import com.ibm.icu.text.CollationElementIterator;
 import com.ibm.icu.text.CollationKey;
 import com.ibm.icu.text.Collator;
+import com.ibm.icu.text.Collator.CollatorFactory;
 import com.ibm.icu.text.RawCollationKey;
 import com.ibm.icu.text.RuleBasedCollator;
 import com.ibm.icu.text.UCharacterIterator;
 import com.ibm.icu.text.UnicodeSet;
-import com.ibm.icu.text.Collator.CollatorFactory;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.VersionInfo;
 
diff --git a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationMiscTest.java b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationMiscTest.java
index f93c8c3..f55ee10 100644
--- a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationMiscTest.java
+++ b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationMiscTest.java
@@ -1,6 +1,6 @@
  /*
  *******************************************************************************
- * Copyright (C) 2002-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2002-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -23,12 +23,12 @@
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.CollationElementIterator;
 import com.ibm.icu.text.CollationKey;
+import com.ibm.icu.text.CollationKey.BoundMode;
 import com.ibm.icu.text.Collator;
 import com.ibm.icu.text.Normalizer;
 import com.ibm.icu.text.RawCollationKey;
 import com.ibm.icu.text.RuleBasedCollator;
 import com.ibm.icu.text.UTF16;
-import com.ibm.icu.text.CollationKey.BoundMode;
 import com.ibm.icu.util.ULocale;
 import com.ibm.icu.util.UResourceBundle;
 
diff --git a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationRegressionTest.java b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationRegressionTest.java
index d830778..7549781 100644
--- a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationRegressionTest.java
+++ b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationRegressionTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2002-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2002-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -1133,7 +1133,73 @@
             }
         }
     }
-    
+
+    // Fixing the infinite loop for surrogates
+    public void Test8484()
+    {
+        String s = "\u9FE1\uCEF3\u2798\uAAB6\uDA7C";
+        Collator coll = Collator.getInstance();
+        CollationKey collKey = coll.getCollationKey(s); 
+        logln("Pass: " + collKey.toString() + " generated OK.");
+    }
+
+    /*
+     * Test case for ticket#8624
+     * Bad collation key with upper first option.
+     */
+    public void TestCaseFirstCompression() {
+        RuleBasedCollator col = (RuleBasedCollator)Collator.getInstance(Locale.US);
+
+        // Default
+        caseFirstCompressionSub(col, "default");
+
+        // Upper first
+        col.setUpperCaseFirst(true);
+        caseFirstCompressionSub(col, "upper first");
+
+        // Lower first
+        col.setLowerCaseFirst(true);
+        caseFirstCompressionSub(col, "lower first");
+    }
+
+    /*
+     * Compare two strings - "aaa...A" and "aaa...a" with
+     * Collation#compare and CollationKey#compareTo, called from
+     * TestCaseFirstCompression.
+     */
+    private void caseFirstCompressionSub(RuleBasedCollator col, String opt) {
+        final int maxLength = 50;
+
+        StringBuilder buf1 = new StringBuilder();
+        StringBuilder buf2 = new StringBuilder();
+        String str1, str2;
+
+        for (int n = 1; n <= maxLength; n++) {
+            buf1.setLength(0);
+            buf2.setLength(0);
+
+            for (int i = 0; i < n - 1; i++) {
+                buf1.append('a');
+                buf2.append('a');
+            }
+            buf1.append('A');
+            buf2.append('a');
+
+            str1 = buf1.toString();
+            str2 = buf2.toString();
+
+            CollationKey key1 = col.getCollationKey(str1);
+            CollationKey key2 = col.getCollationKey(str2);
+
+            int cmpKey = key1.compareTo(key2);
+            int cmpCol = col.compare(str1, str2);
+
+            if ((cmpKey < 0 && cmpCol >= 0) || (cmpKey > 0 && cmpCol <= 0) || (cmpKey == 0 && cmpCol != 0)) {
+                errln("Inconsistent comparison(" + opt + "): str1=" + str1 + ", str2=" + str2 + ", cmpKey=" + cmpKey + " , cmpCol=" + cmpCol);
+            }
+        }
+    }
+
     /* RuleBasedCollator not subclassable
      * @bug 4146160
     //
diff --git a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationTest.java b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationTest.java
index f636399..e94abfa 100644
--- a/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationTest.java
+++ b/main/tests/collate/src/com/ibm/icu/dev/test/collator/CollationTest.java
@@ -1,6 +1,6 @@
 /**
  *******************************************************************************
- * Copyright (C) 2001-2006, International Business Machines Corporation and    *
+ * Copyright (C) 2001-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -12,8 +12,8 @@
 import java.util.Vector;
 
 import com.ibm.icu.dev.test.ModuleTest;
-import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.dev.test.TestDataModule.DataMap;
+import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.lang.UCharacter;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegression.java b/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegression.java
index ff03feb..1e63f59 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegression.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegression.java
@@ -1,6 +1,6 @@
 /**
  *******************************************************************************
- * Copyright (C) 2000-2009, International Business Machines Corporation and    *
+ * Copyright (C) 2000-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -1658,23 +1658,22 @@
     }
 
     /**
-     * WEEK_OF_YEAR computed incorrectly. A failure of this test can indicate a
-     * problem in several different places in the
+     * WEEK_OF_YEAR computed incorrectly. A failure of this test can indicate a problem in several different places in
+     * the
      */
-    public void Test4288792() throws Exception 
-    {
-    TimeZone savedTZ = TimeZone.getDefault();
-    TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
-    GregorianCalendar cal = new GregorianCalendar();
-        
-    for (int i = 1900; i < 2100; i++) {
-        for (int j1 = 1; j1 <= 7; j1++) {
-        // Loop for MinimalDaysInFirstWeek: 1..7
-        for (int j = Calendar.SUNDAY; j <= Calendar.SATURDAY; j++) {
-            // Loop for FirstDayOfWeek: SUNDAY..SATURDAY
-            cal.clear();
-            cal.setMinimalDaysInFirstWeek(j1);
-            cal.setFirstDayOfWeek(j);
+    public void Test4288792() throws Exception {
+        TimeZone savedTZ = TimeZone.getDefault();
+        TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
+        GregorianCalendar cal = new GregorianCalendar();
+
+        for (int i = 1900; i < 2100; i++) {
+            for (int j1 = 1; j1 <= 7; j1++) {
+                // Loop for MinimalDaysInFirstWeek: 1..7
+                for (int j = Calendar.SUNDAY; j <= Calendar.SATURDAY; j++) {
+                    // Loop for FirstDayOfWeek: SUNDAY..SATURDAY
+                    cal.clear();
+                    cal.setMinimalDaysInFirstWeek(j1);
+                    cal.setFirstDayOfWeek(j);
                     // Set the calendar to the first day of the last week
                     // of the year. This may overlap some of the start of
                     // the next year; that is, the last week of 1999 may
@@ -1683,31 +1682,33 @@
                     // get(WEEK_OF_YEAR). The result should be the same
                     // for the whole week. Note that a bug in
                     // getActualMaximum() will break this test.
-            cal.set(Calendar.YEAR, i);
-            int maxWeek = cal.getActualMaximum(Calendar.WEEK_OF_YEAR);
-            cal.set(Calendar.WEEK_OF_YEAR, maxWeek);
-            cal.set(Calendar.DAY_OF_WEEK, j);
-            for (int k = 1; k < 7; k++) {
-            cal.add(Calendar.DATE, 1);
-            int WOY = cal.get(Calendar.WEEK_OF_YEAR);
-            if (WOY != maxWeek) {
-                errln(cal.getTime() + ",got=" + WOY
-                  + ",expected=" + maxWeek 
-                  + ",min=" + j1 + ",first=" + j);
-            }
-            }
+
+                    // Set date to the mid year first before getActualMaximum(WEEK_OF_YEAR).
+                    // getActualMaximum(WEEK_OF_YEAR) is based on the current calendar's
+                    // year of week of year. After clear(), calendar is set to January 1st,
+                    // which may belongs to previous year of week of year.
+                    cal.set(i, Calendar.JULY, 1);
+                    int maxWeek = cal.getActualMaximum(Calendar.WEEK_OF_YEAR);
+                    cal.set(Calendar.WEEK_OF_YEAR, maxWeek);
+                    cal.set(Calendar.DAY_OF_WEEK, j);
+                    for (int k = 1; k < 7; k++) {
+                        cal.add(Calendar.DATE, 1);
+                        int WOY = cal.get(Calendar.WEEK_OF_YEAR);
+                        if (WOY != maxWeek) {
+                            errln(cal.getTime() + ",got=" + WOY + ",expected=" + maxWeek + ",min=" + j1 + ",first=" + j);
+                        }
+                    }
                     // Now advance the calendar one more day. This should
                     // put it at the first day of week 1 of the next year.
-            cal.add(Calendar.DATE, 1);
-            int WOY = cal.get(Calendar.WEEK_OF_YEAR);
-            if (WOY != 1) {
-            errln(cal.getTime() + ",got=" + WOY 
-                  + ",expected=1,min=" + j1 + ",first" + j);
+                    cal.add(Calendar.DATE, 1);
+                    int WOY = cal.get(Calendar.WEEK_OF_YEAR);
+                    if (WOY != 1) {
+                        errln(cal.getTime() + ",got=" + WOY + ",expected=1,min=" + j1 + ",first" + j);
+                    }
+                }
             }
         }
-        }
-    }
-    TimeZone.setDefault(savedTZ);
+        TimeZone.setDefault(savedTZ);
     }
 
     /**
@@ -2153,6 +2154,34 @@
         }
     }
 
+
+    /*
+     * Test case for ticket#8596.
+     * Setting an year followed by getActualMaximum(Calendar.WEEK_OF_YEAR)
+     * may result wrong maximum week.
+     */
+    public void TestT8596() {
+        GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT"));
+        gc.setFirstDayOfWeek(Calendar.MONDAY);
+        gc.setMinimalDaysInFirstWeek(4);
+
+        // Force the calender to resolve the fields once.
+        // The maximum week number in 2011 is 52.
+        gc.set(Calendar.YEAR, 2011);
+        gc.get(Calendar.YEAR);
+
+        // Set a date in year 2009, but not calling get to resolve
+        // the calendar's internal field yet.
+        gc.set(2009, Calendar.JULY, 1);
+
+        // Then call getActuamMaximum for week of year.
+        // #8596 was caused by conflict between year set
+        // above and internal work calendar field resolution.
+        int maxWeeks = gc.getActualMaximum(Calendar.WEEK_OF_YEAR);
+        if (maxWeeks != 53) {
+            errln("FAIL: Max week in 2009 in ISO calendar is 53, but got " + maxWeeks);
+        }
+    }
 }
 
 //eof
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/calendar/IndianTest.java b/main/tests/core/src/com/ibm/icu/dev/test/calendar/IndianTest.java
index 0e6e3fc..d8633f9 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/calendar/IndianTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/calendar/IndianTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2005-2008, International Business Machines Corporation and    *
+ * Copyright (C) 2005-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -11,6 +11,7 @@
 
 import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.GregorianCalendar;
 import com.ibm.icu.util.IndianCalendar;
@@ -240,4 +241,36 @@
         doLimitsTest(indian, null, cal.getTime());
         doTheoreticalLimitsTest(indian, true);
     }
+    
+    /**
+     * Problem reported by Bruno Haible <bruno.haible@de.ibm.com>
+     *  -- see ticket 8419 -- http://bugs.icu-project.org/trac/ticket/8419
+     * Problem with months out of range 0-11
+     */
+    public void TestYearEdge() {
+        // Display dates in ISO 8601 format.
+        DateFormat fmt = new SimpleDateFormat("YYYY-MM-dd", ULocale.US);
+
+        // Instantiate an Indian calendar.
+        ULocale locale = ULocale.US.setKeywordValue("calendar", "indian");
+        Calendar cal = Calendar.getInstance(locale);
+
+        // Try add() repeatedly.
+        cal.setTimeInMillis(1295568000000L);
+        if (!fmt.format(cal.getTime()).equals("2011-01-20")){
+            errln("Incorrect calendar value for year edge test");
+        }
+        cal.add(Calendar.MONTH, 1);
+        if (!fmt.format(cal.getTime()).equals("2011-02-19")){
+            errln("Incorrect calendar value for year edge test");
+        }
+        cal.add(Calendar.MONTH, 1);
+        if (!fmt.format(cal.getTime()).equals("2011-03-21")){
+            errln("Incorrect calendar value for year edge test");
+        }
+        cal.add(Calendar.MONTH, 1);
+        if (!fmt.format(cal.getTime()).equals("2011-04-20")){
+            errln("Incorrect calendar value for year edge test");
+        }
+    }
 }
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 1946403..fdccb87 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
@@ -26,12 +26,12 @@
 
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.text.ChineseDateFormat;
+import com.ibm.icu.text.ChineseDateFormat.Field;
 import com.ibm.icu.text.ChineseDateFormatSymbols;
 import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DateFormatSymbols;
 import com.ibm.icu.text.NumberFormat;
 import com.ibm.icu.text.SimpleDateFormat;
-import com.ibm.icu.text.ChineseDateFormat.Field;
 import com.ibm.icu.util.BuddhistCalendar;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.ChineseCalendar;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java b/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
index 3f4b37f..7b65330 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.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.                                                *
  *******************************************************************************
  */
@@ -21,8 +21,8 @@
 import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DateIntervalFormat;
 import com.ibm.icu.text.DateIntervalInfo;
-import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.text.DateIntervalInfo.PatternInfo;
+import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.DateInterval;
 import com.ibm.icu.util.ULocale;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java b/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
index f70de74..7ebe30d 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2006-2010, Google, International Business Machines Corporation *
+ * Copyright (C) 2006-2011, Google, International Business Machines Corporation *
  * and others. All Rights Reserved.                                            *
  *******************************************************************************
  */
@@ -24,11 +24,11 @@
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
+import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
 import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.text.UTF16;
 import com.ibm.icu.text.UnicodeSet;
-import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
-import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.GregorianCalendar;
 import com.ibm.icu.util.SimpleTimeZone;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDateFormatAPI.java b/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDateFormatAPI.java
index c25a17f..8ef2f44 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDateFormatAPI.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDateFormatAPI.java
@@ -1,6 +1,6 @@
 /*****************************************************************************************
  *
- *   Copyright (C) 1996-2010, International Business Machines
+ *   Copyright (C) 1996-2011, International Business Machines
  *   Corporation and others.  All Rights Reserved.
  **/
 
@@ -26,6 +26,7 @@
 import com.ibm.icu.text.NumberFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.TimeZone;
+import com.ibm.icu.util.VersionInfo;
 
 public class IntlTestDateFormatAPI extends com.ibm.icu.dev.test.TestFmwk
 {
@@ -152,6 +153,7 @@
 
         // Ticket#6280
         // These locales should be included in the result
+        boolean java7orLater = (VersionInfo.javaVersion().compareTo(VersionInfo.getInstance(1, 7)) >= 0);
         final Locale[] samples = {
                 new Locale("zh", "CN"),
                 new Locale("zh", "TW"),
@@ -172,7 +174,13 @@
         }
         for (int i = 0; i < available.length; i++) {
             if (!available[i]) {
-                errln("ERROR: missing Locale: " + samples[i]);
+                if (java7orLater) {
+                    // Java 7 supports script field, so zh_Hans_CN is included
+                    // in the available locale list.
+                    logln("INFO: missing Locale: " + samples[i]);
+                } else {
+                    errln("ERROR: missing Locale: " + samples[i]);
+                }
             }
         }
 
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
index e8e743d..7bbcc66 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
@@ -1617,6 +1617,29 @@
         }
     }
 
+    public void TestBigDecimalRounding() {
+	String figure = "50.000000004"; 
+	Double dbl = new Double(figure); 
+	BigDecimal dec = new BigDecimal(figure);
+
+	DecimalFormat f = (DecimalFormat) NumberFormat.getInstance(); 
+	f.applyPattern("00.00######");
+
+	assertEquals("double format", "50.00", f.format(dbl));
+	assertEquals("bigdec format", "50.00", f.format(dec));
+
+	int maxFracDigits = f.getMaximumFractionDigits();
+	BigDecimal roundingIncrement = new BigDecimal("1").movePointLeft(maxFracDigits);
+
+	f.setRoundingIncrement(roundingIncrement);
+	f.setRoundingMode(BigDecimal.ROUND_DOWN); 
+	assertEquals("Rounding down", f.format(dbl), f.format(dec));
+
+	f.setRoundingIncrement(roundingIncrement);
+	f.setRoundingMode(BigDecimal.ROUND_HALF_UP); 
+	assertEquals("Rounding half up", f.format(dbl), f.format(dec));
+    }
+
     void checkRounding(DecimalFormat nf, BigDecimal base, int iterations, BigDecimal increment) {
         nf.setRoundingIncrement(increment.toBigDecimal());
         BigDecimal lastParsed = new BigDecimal(Integer.MIN_VALUE); // used to make sure that rounding is monotonic
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetStringSpanTest.java b/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetStringSpanTest.java
index 35f76da..4bf1b90 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetStringSpanTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetStringSpanTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -10,8 +10,8 @@
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.UTF16;
 import com.ibm.icu.text.UnicodeSet;
-import com.ibm.icu.text.UnicodeSetIterator;
 import com.ibm.icu.text.UnicodeSet.SpanCondition;
+import com.ibm.icu.text.UnicodeSetIterator;
 
 /**
  * @test
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetTest.java b/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetTest.java
index f6d2935..533ea25 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/lang/UnicodeSetTest.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.                                                *
  *******************************************************************************
  */
@@ -25,15 +25,15 @@
 import com.ibm.icu.impl.SortedSetRelation;
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.lang.UCharacterEnums.ECharacterCategory;
 import com.ibm.icu.lang.UProperty;
 import com.ibm.icu.lang.UScript;
-import com.ibm.icu.lang.UCharacterEnums.ECharacterCategory;
 import com.ibm.icu.text.SymbolTable;
 import com.ibm.icu.text.UTF16;
 import com.ibm.icu.text.UnicodeMatcher;
 import com.ibm.icu.text.UnicodeSet;
-import com.ibm.icu.text.UnicodeSetIterator;
 import com.ibm.icu.text.UnicodeSet.ComparisonStyle;
+import com.ibm.icu.text.UnicodeSetIterator;
 
 /**
  * @test
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTest.java b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTest.java
index 15463d5..0b0297d 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTest.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.                                                *
  *******************************************************************************
  */
@@ -16,24 +16,24 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
-import java.util.Map.Entry;
 
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.impl.ICULocaleService;
+import com.ibm.icu.impl.ICULocaleService.ICUResourceBundleFactory;
+import com.ibm.icu.impl.ICULocaleService.LocaleKey;
+import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
 import com.ibm.icu.impl.ICUNotifier;
 import com.ibm.icu.impl.ICURWLock;
 import com.ibm.icu.impl.ICUResourceBundle;
 import com.ibm.icu.impl.ICUService;
-import com.ibm.icu.impl.LocaleUtility;
-import com.ibm.icu.impl.ICULocaleService.ICUResourceBundleFactory;
-import com.ibm.icu.impl.ICULocaleService.LocaleKey;
-import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
 import com.ibm.icu.impl.ICUService.Factory;
 import com.ibm.icu.impl.ICUService.Key;
 import com.ibm.icu.impl.ICUService.ServiceListener;
 import com.ibm.icu.impl.ICUService.SimpleFactory;
+import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.util.ULocale;
 
 public class ICUServiceTest extends TestFmwk
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTestSample.java b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTestSample.java
index fe87bce..bd7f6de 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTestSample.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceTestSample.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.                                                *
  *******************************************************************************
  */
@@ -10,9 +10,9 @@
 import java.util.EventListener;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
-import java.util.Map.Entry;
 
 import com.ibm.icu.impl.ICULocaleService;
 import com.ibm.icu.impl.ICUService;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceThreadTest.java b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceThreadTest.java
index 960bb6d..70c68e9 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceThreadTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/ICUServiceThreadTest.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.                                                *
  *******************************************************************************
  */
@@ -16,11 +16,11 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.MissingResourceException;
 import java.util.Random;
 import java.util.Set;
 import java.util.SortedMap;
-import java.util.Map.Entry;
 
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.dev.test.TestLog;
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleBuilderTest.java b/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleBuilderTest.java
index b6d8691..1ab51e3 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleBuilderTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleBuilderTest.java
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009-2010, International Business Machines Corporation and    *
+ * Copyright (C) 2009-2011, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -23,56 +23,96 @@
     }
 
     public void TestLocaleBuilder() {
-        // First String "st": strict (default) / "lv": lenient variant
         // "L": +1 = language
         // "S": +1 = script
         // "R": +1 = region
         // "V": +1 = variant
-        // "K": +1 = LDML key / +2 = LDML type
+        // "K": +1 = Unicode locale key / +2 = Unicode locale type
+        // "A": +1 = Unicode locale attribute
         // "E": +1 = extension letter / +2 = extension value
         // "P": +1 = private use
+        // "U": +1 = ULocale
+        // "B": +1 = BCP47 language tag
+
+        // "C": Clear all
+        // "N": Clear extensions
+        // "D": +1 = Unicode locale attribute to be removed
+
         // "X": indicates an exception must be thrown
-        // "T": +1 = expected language tag
+        // "T": +1 = expected language tag / +2 = expected locale string
         String[][] TESTCASE = {
-            {"st", "L", "en", "R", "us", "T", "en-US", "en_US"},
-            {"st", "L", "en", "R", "FR", "L", "fr", "T", "fr-FR", "fr_FR"},
-            {"st", "L", "123", "X"},
-            {"st", "R", "us", "T", "und-US", "_US"},
-            {"st", "R", "usa", "X"},
-            {"st", "R", "123", "L", "en", "T", "en-123", "en_123"},
-            {"st", "S", "LATN", "L", "DE", "T", "de-Latn", "de_Latn"},
-            {"st", "S", "latin", "X"},
-            {"st", "L", "th", "R", "th", "K", "nu", "thai", "T", "th-TH-u-nu-thai", "th_TH@numbers=thai"},
-            {"st", "E", "z", "ExtZ", "L", "en", "T", "en-z-extz", "en@z=extz"},
-            {"st", "L", "fr", "R", "FR", "P", "Yoshito-ICU", "T", "fr-FR-x-yoshito-icu", "fr_FR@x=yoshito-icu"},
-            {"st", "L", "ja", "R", "jp", "K", "ca", "japanese", "T", "ja-JP-u-ca-japanese", "ja_JP@calendar=japanese"},
-            {"st", "K", "co", "PHONEBK", "K", "ca", "gregory", "L", "De", "T", "de-u-ca-gregory-co-phonebk", "de@calendar=gregorian;collation=phonebook"},
-            {"st", "E", "o", "OPQR", "E", "a", "aBcD", "T", "und-a-abcd-o-opqr", "@a=abcd;o=opqr"},
-            {"st", "E", "u", "nu-thai-ca-gregory", "L", "TH", "T", "th-u-ca-gregory-nu-thai", "th@calendar=gregorian;numbers=thai"},
-            {"st", "L", "en", "K", "tz", "usnyc", "R", "US", "T", "en-US-u-tz-usnyc", "en_US@timezone=America/New_York"},
-            {"st", "L", "de", "K", "co", "phonebk", "K", "ks", "level1", "K", "kk", "true", "T", "de-u-co-phonebk-kk-true-ks-level1", "de@collation=phonebook;colnormalization=yes;colstrength=primary"},
-            {"lv", "L", "en", "R", "us", "V", "Windows_XP", "T", "en-US-windows-x-variant-xp", "en_US_WINDOWS_XP"},
+                {"L", "en", "R", "us", "T", "en-US", "en_US"},
+                {"L", "en", "R", "CA", "L", null, "T", "und-CA", "_CA"},
+                {"L", "en", "R", "CA", "L", "", "T", "und-CA", "_CA"},
+                {"L", "en", "R", "FR", "L", "fr", "T", "fr-FR", "fr_FR"},
+                {"L", "123", "X"},
+                {"R", "us", "T", "und-US", "_US"},
+                {"R", "usa", "X"},
+                {"R", "123", "L", "it", "R", null, "T", "it", "it"},
+                {"R", "123", "L", "it", "R", "", "T", "it", "it"},
+                {"R", "123", "L", "en", "T", "en-123", "en_123"},
+                {"S", "LATN", "L", "DE", "T", "de-Latn", "de_Latn"},
+                {"L", "De", "S", "latn", "R", "de", "S", "", "T", "de-DE", "de_DE"},
+                {"L", "De", "S", "latn", "R", "de", "S", null, "T", "de-DE", "de_DE"},
+                {"S", "latin", "X"},
+                {"V", "1234", "L", "en", "T", "en-1234", "en__1234"},
+                {"V", "1234", "L", "en", "V", "5678", "T", "en-5678", "en__5678"},
+                {"V", "1234", "L", "en", "V", null, "T", "en", "en"},
+                {"V", "1234", "L", "en", "V", "", "T", "en", "en"},
+                {"V", "123", "X"},
+                {"U", "en_US", "T", "en-US", "en_US"},
+                {"U", "en_US_WIN", "X"},
+                {"B", "fr-FR-1606nict-u-ca-gregory-x-test", "T", "fr-FR-1606nict-u-ca-gregory-x-test", "fr_FR_1606NICT@calendar=gregorian;x=test"},
+                {"B", "ab-cde-fghij", "T", "cde-fghij", "cde__FGHIJ"},
+                {"B", "und-CA", "T", "und-CA", "_CA"},
+                {"B", "en-US-x-test-lvariant-var", "T", "en-US-x-test-lvariant-var", "en_US_VAR@x=test"},
+                {"B", "en-US-VAR", "X"},
+                {"U", "ja_JP@calendar=japanese;currency=JPY", "L", "ko", "T", "ko-JP-u-ca-japanese-cu-jpy", "ko_JP@calendar=japanese;currency=jpy"},
+                {"U", "ja_JP@calendar=japanese;currency=JPY", "K", "ca", null, "T", "ja-JP-u-cu-jpy", "ja_JP@currency=jpy"},
+                {"U", "ja_JP@calendar=japanese;currency=JPY", "E", "u", "attr1-ca-gregory", "T", "ja-JP-u-attr1-ca-gregory", "ja_JP@attribute=attr1;calendar=gregorian"},
+                {"U", "en@colnumeric=yes", "K", "kn", "", "T", "en-u-kn-true", "en@colnumeric=yes"},
+                {"L", "th", "R", "th", "K", "nu", "thai", "T", "th-TH-u-nu-thai", "th_TH@numbers=thai"},
+                {"U", "zh_Hans", "R", "sg", "K", "ca", "badcalendar", "X"},
+                {"U", "zh_Hans", "R", "sg", "K", "cal", "gregory", "X"},
+                {"E", "z", "ExtZ", "L", "en", "T", "en-z-extz", "en@z=extz"},
+                {"E", "z", "ExtZ", "L", "en", "E", "z", "", "T", "en", "en"},
+                {"E", "z", "ExtZ", "L", "en", "E", "z", null, "T", "en", "en"},
+                {"E", "a", "x", "X"},
+                {"E", "a", "abc_def", "T", "und-a-abc-def", "@a=abc-def"},
+                {"L", "en", "E", "u", "bbb-aaa-00", "T", "en-u-aaa-bbb-00-true", "en@00=true;attribute=aaa-bbb"},
+                {"L", "fr", "R", "FR", "P", "Yoshito-ICU", "T", "fr-FR-x-yoshito-icu", "fr_FR@x=yoshito-icu"},
+                {"L", "ja", "R", "jp", "K", "ca", "japanese", "T", "ja-JP-u-ca-japanese", "ja_JP@calendar=japanese"},
+                {"K", "co", "PHONEBK", "K", "ca", "gregory", "L", "De", "T", "de-u-ca-gregory-co-phonebk", "de@calendar=gregorian;collation=phonebook"},
+                {"E", "o", "OPQR", "E", "a", "aBcD", "T", "und-a-abcd-o-opqr", "@a=abcd;o=opqr"},
+                {"E", "u", "nu-thai-ca-gregory", "L", "TH", "T", "th-u-ca-gregory-nu-thai", "th@calendar=gregorian;numbers=thai"},
+                {"L", "en", "K", "tz", "usnyc", "R", "US", "T", "en-US-u-tz-usnyc", "en_US@timezone=America/New_York"},
+                {"L", "de", "K", "co", "phonebk", "K", "ks", "level1", "K", "kk", "true", "T", "de-u-co-phonebk-kk-true-ks-level1", "de@collation=phonebook;colnormalization=yes;colstrength=primary"},
+                {"L", "en", "R", "US", "K", "ca", "gregory", "T", "en-US-u-ca-gregory", "en_US@calendar=gregorian"},
+                {"L", "en", "R", "US", "K", "cal", "gregory", "X"},
+                {"L", "en", "R", "US", "K", "ca", "gregorian", "X"},
+                {"L", "en", "R", "US", "K", "kn", "", "T", "en-US-u-kn-true", "en_US@colnumeric=yes"},
+                {"B", "de-DE-u-co-phonebk", "C", "L", "pt", "T", "pt", "pt"},
+                {"B", "ja-jp-u-ca-japanese", "N", "T", "ja-JP", "ja_JP"},
+                {"B", "es-u-def-abc-co-trad", "A", "hij", "D", "def", "T", "es-u-abc-hij-co-trad", "es@attribute=abc-hij;collation=traditional"},
+                {"B", "es-u-def-abc-co-trad", "A", "hij", "D", "def", "D", "def", "T", "es-u-abc-hij-co-trad", "es@attribute=abc-hij;collation=traditional"},
+                {"L", "en", "A", "aa", "X"},
+                {"B", "fr-u-attr1-cu-eur", "D", "attribute1", "X"},
         };
 
         Builder bld_st = new Builder();
-        Builder bld_lv = new Builder(true);
 
         for (int tidx = 0; tidx < TESTCASE.length; tidx++) {
             int i = 0;
             String[] expected = null;
 
             Builder bld = bld_st;
-            String bldType = TESTCASE[tidx][i++];
-
-            if (bldType.equals("lv")) {
-                bld = bld_lv;
-            }
 
             bld.clear();
 
             while (true) {
                 String method = TESTCASE[tidx][i++];
                 try {
+                    // setters
                     if (method.equals("L")) {
                         bld.setLanguage(TESTCASE[tidx][i++]);
                     } else if (method.equals("S")) {
@@ -85,13 +125,29 @@
                         String key = TESTCASE[tidx][i++];
                         String type = TESTCASE[tidx][i++];
                         bld.setUnicodeLocaleKeyword(key, type);
+                    } else if (method.equals("A")) {
+                        bld.addUnicodeLocaleAttribute(TESTCASE[tidx][i++]);
                     } else if (method.equals("E")) {
                         String key = TESTCASE[tidx][i++];
                         String value = TESTCASE[tidx][i++];
                         bld.setExtension(key.charAt(0), value);
                     } else if (method.equals("P")) {
                         bld.setExtension(ULocale.PRIVATE_USE_EXTENSION, TESTCASE[tidx][i++]);
-                    } else if (method.equals("X")) {
+                    } else if (method.equals("U")) {
+                        bld.setLocale(new ULocale(TESTCASE[tidx][i++]));
+                    } else if (method.equals("B")) {
+                        bld.setLanguageTag(TESTCASE[tidx][i++]);
+                    }
+                    // clear / remove
+                    else if (method.equals("C")) {
+                        bld.clear();
+                    } else if (method.equals("N")) {
+                        bld.clearExtensions();
+                    } else if (method.equals("D")) {
+                        bld.removeUnicodeLocaleAttribute(TESTCASE[tidx][i++]);
+                    }
+                    // result
+                    else if (method.equals("X")) {
                         errln("FAIL: No excetion was thrown - test csae: "
                                 + Arrays.toString(TESTCASE[tidx]));
                     } else if (method.equals("T")) {
@@ -99,7 +155,11 @@
                         expected[0] = TESTCASE[tidx][i];
                         expected[1] = TESTCASE[tidx][i + 1];
                         break;
+                    } else {
+                        // Unknow test method
+                        errln("Unknown test case method: There is an error in the test case data.");
                     }
+
                 } catch (IllformedLocaleException e) {
                     if (TESTCASE[tidx][i].equals("X")) {
                         // This exception is expected
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java b/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java
index 878d871..db5b057 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java
@@ -1,6 +1,6 @@
 /*
  **********************************************************************
- * Copyright (c) 2002-2009, International Business Machines
+ * Copyright (c) 2002-2011, International Business Machines
  * Corporation and others.  All Rights Reserved.
  **********************************************************************
  * Author: Mark Davis
@@ -15,8 +15,8 @@
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import com.ibm.icu.util.Freezable;
 
diff --git a/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java b/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java
index 34de716..d0ae40e 100644
--- a/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java
+++ b/main/tests/core/src/com/ibm/icu/dev/test/util/ULocaleTest.java
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2010, International Business Machines
+* Copyright (c) 2004-2011, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -12,9 +12,13 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 
 import com.ibm.icu.dev.test.TestFmwk;
@@ -23,18 +27,20 @@
 import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DecimalFormat;
 import com.ibm.icu.text.NumberFormat;
-import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.text.NumberFormat.SimpleNumberFormatFactory;
+import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.IllformedLocaleException;
 import com.ibm.icu.util.LocaleData;
 import com.ibm.icu.util.ULocale;
+import com.ibm.icu.util.ULocale.Builder;
 import com.ibm.icu.util.UResourceBundle;
 import com.ibm.icu.util.VersionInfo;
-import com.ibm.icu.util.ULocale.Builder;
 
 public class ULocaleTest extends TestFmwk {
 
+    private static final boolean JAVA7_OR_LATER = (VersionInfo.javaVersion().compareTo(VersionInfo.getInstance(1, 7)) >= 0);
+
     public static void main(String[] args) throws Exception {
         new ULocaleTest().run(args);
     }
@@ -188,7 +194,8 @@
      */
     public void TestJavaLocaleCompatibility() {
         Locale backupDefault = Locale.getDefault();
-        
+        ULocale orgUlocDefault = ULocale.getDefault();
+
         // Java Locale for ja_JP with Japanese calendar
         Locale jaJPJP = new Locale("ja", "JP", "JP");
         Locale jaJP = new Locale("ja", "JP");
@@ -198,8 +205,8 @@
         Calendar cal = Calendar.getInstance(jaJPJP);
         String caltype = cal.getType();
         if (!caltype.equals("japanese")) {
-            errln("FAIL: Invalid calendar type: " + caltype + " /expected: japanese");
-        }
+                errln("FAIL: Invalid calendar type: " + caltype + " /expected: japanese");
+            }
 
         cal = Calendar.getInstance(jaJP);
         caltype = cal.getType();
@@ -210,21 +217,27 @@
         // Default locale
         Locale.setDefault(jaJPJP);
         ULocale defUloc = ULocale.getDefault();
+        if (JAVA7_OR_LATER) {
+            if (!defUloc.toString().equals("ja_JP_JP@calendar=japanese")) {
+                errln("FAIL: Invalid default ULocale: " + defUloc + " /expected: ja_JP_JP@calendar=japanese");
+            }
+        } else {
         if (!defUloc.toString().equals("ja_JP@calendar=japanese")) {
-            errln("FAIL: Invalid default ULocale: " + defUloc + " /expected: ja_JP@calendar=japanese");
+                errln("FAIL: Invalid default ULocale: " + defUloc + " /expected: ja_JP@calendar=japanese");
+            }
         }
         // Check calendar type
         cal = Calendar.getInstance();
         caltype = cal.getType();
         if (!caltype.equals("japanese")) {
-            errln("FAIL: Invalid calendar type: " + caltype + " /expected: japanese");
-        }
+                errln("FAIL: Invalid calendar type: " + caltype + " /expected: japanese");
+            }
         Locale.setDefault(backupDefault);
 
         // Set default via ULocale
         ULocale ujaJP_calJP = new ULocale("ja_JP@calendar=japanese");
         ULocale.setDefault(ujaJP_calJP);
-        if (!Locale.getDefault().equals(jaJPJP)) {
+        if (!JAVA7_OR_LATER && !Locale.getDefault().equals(jaJPJP)) {
             errln("FAIL: ULocale#setDefault failed to set Java Locale ja_JP_JP /actual: " + Locale.getDefault());
         }
         // Ticket#6672 - missing keywords
@@ -243,7 +256,7 @@
 
         // We also want to map ICU locale ja@calendar=japanese to Java ja_JP_JP
         ULocale.setDefault(new ULocale("ja@calendar=japanese"));
-        if (!Locale.getDefault().equals(jaJPJP)) {
+        if (!JAVA7_OR_LATER && !Locale.getDefault().equals(jaJPJP)) {
             errln("FAIL: ULocale#setDefault failed to set Java Locale ja_JP_JP /actual: " + Locale.getDefault());
         }
         Locale.setDefault(backupDefault);
@@ -252,31 +265,36 @@
         Locale noNONY = new Locale("no", "NO", "NY");
         Locale.setDefault(noNONY);
         defUloc = ULocale.getDefault();
-        if (defUloc.toString().equals("nn_NY")) {
-            errln("FAIL: Invalid default ULocale: " + defUloc + " /expected: nn_NY");
+        if (!defUloc.toString().equals("nn_NO")) {
+            errln("FAIL: Invalid default ULocale: " + defUloc + " /expected: nn_NO");
         }
         Locale.setDefault(backupDefault);
 
         // Java th_TH_TH -> ICU th_TH@numbers=thai
         ULocale.setDefault(new ULocale("th@numbers=thai"));
-        if (!Locale.getDefault().equals(thTHTH)) {
+        if (!JAVA7_OR_LATER && !Locale.getDefault().equals(thTHTH)) {
             errln("FAIL: ULocale#setDefault failed to set Java Locale th_TH_TH /actual: " + Locale.getDefault());
         }
         Locale.setDefault(backupDefault);
 
         // Set default via ULocale
         ULocale.setDefault(new ULocale("nn_NO"));
-        if (!Locale.getDefault().equals(noNONY)) {
+        if (!JAVA7_OR_LATER && !Locale.getDefault().equals(noNONY)) {
             errln("FAIL: ULocale#setDefault failed to set Java Locale no_NO_NY /actual: " + Locale.getDefault());
         }
         Locale.setDefault(backupDefault);        
 
         // We also want to map ICU locale nn to Java no_NO_NY
         ULocale.setDefault(new ULocale("nn"));
-        if (!Locale.getDefault().equals(noNONY)) {
+        if (!JAVA7_OR_LATER && !Locale.getDefault().equals(noNONY)) {
             errln("FAIL: ULocale#setDefault failed to set Java Locale no_NO_NY /actual: " + Locale.getDefault());
         }
         Locale.setDefault(backupDefault);
+
+        // Make sure default ULocale is restored
+        if (!ULocale.getDefault().equals(orgUlocDefault)) {
+            errln("FAIL: Original default ULocale is not restored - " + ULocale.getDefault() + ", expected(orginal) - " + orgUlocDefault);
+        }
     }
     
     // ================= Infrastructure =================
@@ -3716,26 +3734,28 @@
             {"en_US",       "en-US"},
             {"iw_IL",       "he-IL"},
             {"sr_Latn_SR",  "sr-Latn-SR"},
-            {"en__POSIX",   "en-posix"},
-            // {"en_POSIX",    "en"}, /* ICU4J locale parser successfully parse en_POSIX as language:en/variant:POSIX */
+            {"en_US_POSIX@ca=japanese", "en-US-u-ca-japanese-va-posix"},
+            {"en__POSIX",   "en-u-va-posix"},
+            {"en_US_POSIX_VAR", "en-US-posix-x-lvariant-var"},  // variant POSIX_VAR is processed as regular variant
+            {"en_US_VAR_POSIX", "en-US-x-lvariant-var-posix"},  // variant VAR_POSIX is processed as regular variant
+            {"en_US_POSIX@va=posix2",   "en-US-u-va-posix2"},   // if keyword va=xxx already exists, variant POSIX is simply dropped
             {"und_555",     "und-555"},
             {"123",         "und"},
             {"%$#&",        "und"},
             {"_Latn",       "und-Latn"},
             {"_DE",         "und-DE"},
             {"und_FR",      "und-FR"},
-            {"th_TH_TH",    "th-TH-x-variant-th"},
+            {"th_TH_TH",    "th-TH-x-lvariant-th"},
             {"bogus",       "bogus"},
             {"foooobarrr",  "und"},
-            //{"az_AZ_CYRL",  "az-cyrl-az"}, /* ICU4J does not have this specia locale mapping */
-            {"aa_BB_CYRL",  "aa-BB-x-variant-cyrl"},
+            {"aa_BB_CYRL",  "aa-BB-x-lvariant-cyrl"},
             {"en_US_1234",  "en-US-1234"},
             {"en_US_VARIANTA_VARIANTB", "en-US-varianta-variantb"},
             {"en_US_VARIANTB_VARIANTA", "en-US-variantb-varianta"},
             {"ja__9876_5432",   "ja-9876-5432"},
-            {"zh_Hant__VAR",    "zh-Hant-x-variant-var"},
+            {"zh_Hant__VAR",    "zh-Hant-x-lvariant-var"},
             {"es__BADVARIANT_GOODVAR",  "es"},
-            {"es__GOODVAR_BAD_BADVARIANT",  "es-goodvar-x-variant-bad"},
+            {"es__GOODVAR_BAD_BADVARIANT",  "es-goodvar-x-lvariant-bad"},
             {"en@calendar=gregorian",   "en-u-ca-gregory"},
             {"de@collation=phonebook;calendar=gregorian",   "de-u-ca-gregory-co-phonebk"},
             {"th@numbers=thai;z=extz;x=priv-use;a=exta",   "th-a-exta-u-nu-thai-z-extz-x-priv-use"},
@@ -3743,6 +3763,10 @@
             {"en@timezone=US/Eastern",    "en-u-tz-usnyc"},
             {"en@x=x-y-z;a=a-b-c",  "en-x-x-y-z"},
             {"it@collation=badcollationtype;colStrength=identical;cu=usd-eur", "it-u-ks-identic"},
+            {"en_US_POSIX", "en-US-u-va-posix"},
+            {"en_US_POSIX@calendar=japanese;currency=EUR","en-US-u-ca-japanese-cu-eur-va-posix"},
+            {"@x=elmer",    "x-elmer"},
+            {"_US@x=elmer", "und-US-x-elmer"},
         };
 
         for (int i = 0; i < locale_to_langtag.length; i++) {
@@ -3774,7 +3798,7 @@
             {"zh-cmn-CH",           "cmn_CH",               NOERROR},
             {"xxx-yy",              "xxx_YY",               NOERROR},
             {"fr-234",              "fr_234",               NOERROR},
-            {"i-default",           "",                     NOERROR},
+            {"i-default",           "en@x=i-default",       NOERROR},
             {"i-test",              "",                     Integer.valueOf(0)},
             {"ja-jp-jp",            "ja_JP",                Integer.valueOf(6)},
             {"bogus",               "bogus",                NOERROR},
@@ -3784,15 +3808,21 @@
             {"und-varzero-var1-vartwo", "__VARZERO",        Integer.valueOf(12)},
             {"en-u-ca-gregory",     "en@calendar=gregorian",    NOERROR},
             {"en-U-cu-USD",         "en@currency=usd",      NOERROR},
+            {"en-us-u-va-posix",    "en_US_POSIX",          NOERROR},
+            {"en-us-u-ca-gregory-va-posix", "en_US_POSIX@calendar=gregorian",   NOERROR},
+            {"en-us-posix-u-va-posix",  "en_US_POSIX@va=posix", NOERROR},
+            {"en-us-u-va-posix2",   "en_US@va=posix2",      NOERROR},
+            {"en-us-vari1-u-va-posix",   "en_US_VARI1@va=posix",  NOERROR},
             {"ar-x-1-2-3",          "ar@x=1-2-3",           NOERROR},
             {"fr-u-nu-latn-cu-eur", "fr@currency=eur;numbers=latn", NOERROR},
             {"de-k-kext-u-co-phonebk-nu-latn",  "de@collation=phonebook;k=kext;numbers=latn",   NOERROR},
-            {"ja-u-cu-jpy-ca-jp",   "ja@currency=jpy",      Integer.valueOf(15)},
+            {"ja-u-cu-jpy-ca-jp",   "ja@calendar=true;currency=jpy;jp=true",  NOERROR},
             {"en-us-u-tz-usnyc",    "en_US@timezone=America/New_York",      NOERROR},
             {"und-a-abc-def",       "@a=abc-def",           NOERROR},
             {"zh-u-ca-chinese-x-u-ca-chinese",  "zh@calendar=chinese;x=u-ca-chinese",   NOERROR},
             {"fr--FR",              "fr",                   Integer.valueOf(3)},
             {"fr-",                 "fr",                   Integer.valueOf(3)},
+            {"x-elmer",             "@x=elmer",             NOERROR},
         };
 
         for (int i = 0; i < langtag_to_locale.length; i++) {
@@ -3883,4 +3913,246 @@
             }
         }
     }
+
+    public void TestExtension() {
+        String[][] TESTCASES = {
+                // {"<langtag>", "<ext key1>", "<ext val1>", "<ext key2>", "<ext val2>", ....},
+                {"en"},
+                {"en-a-exta-b-extb", "a", "exta", "b", "extb"},
+                {"en-b-extb-a-exta", "a", "exta", "b", "extb"},
+                {"de-x-a-bc-def", "x", "a-bc-def"},
+                {"ja-JP-u-cu-jpy-ca-japanese-x-java", "u", "ca-japanese-cu-jpy", "x", "java"},
+        };
+
+        for (String[] testcase : TESTCASES) {
+            ULocale loc = ULocale.forLanguageTag(testcase[0]);
+
+            int nExtensions = (testcase.length - 1) / 2;
+
+            Set<Character> keys = loc.getExtensionKeys();
+            if (keys.size() != nExtensions) {
+                errln("Incorrect number of extensions: returned="
+                        + keys.size() + ", expected=" + nExtensions
+                        + ", locale=" + testcase[0]);
+            }
+
+            for (int i = 0; i < nExtensions; i++) {
+                String kstr = testcase[i/2 + 1];
+                String ext = loc.getExtension(Character.valueOf(kstr.charAt(0)));
+                if (ext == null || !ext.equals(testcase[i/2 + 2])) {
+                    errln("Incorrect extension value: key=" 
+                            + kstr + ", returned=" + ext + ", expected=" + testcase[i/2 + 2]
+                            + ", locale=" + testcase[0]);
+                }
+            }
+        }
+
+        // Exception handling
+        boolean sawException = false;
+        try {
+            ULocale l = ULocale.forLanguageTag("en-US-a-exta");
+            l.getExtension('$');
+        } catch (IllegalArgumentException e) {
+            sawException = true;
+        }
+        if (!sawException) {
+            errln("getExtension must throw an exception on illegal input key");
+        }
+    }
+
+    public void TestUnicodeLocaleExtension() {
+        String[][] TESTCASES = {
+                //"<langtag>", "<attr1>,<attr2>,...", "<key1>,<key2>,...", "<type1>", "<type2>", ...},
+                {"en", null, null},
+                {"en-a-ext1-x-privuse", null, null},
+                {"en-u-attr1-attr2", "attr1,attr2", null},
+                {"ja-u-ca-japanese-cu-jpy", null, "ca,cu", "japanese", "jpy"},
+                {"th-TH-u-number-attr-nu-thai-ca-buddhist", "attr,number", "ca,nu", "buddhist", "thai"},
+        };
+
+        for (String[] testcase : TESTCASES) {
+            ULocale loc = ULocale.forLanguageTag(testcase[0]);
+
+            Set<String> expectedAttributes = new HashSet<String>();
+            if (testcase[1] != null) {
+                String[] attrs = testcase[1].split(",");
+                for (String s : attrs) {
+                    expectedAttributes.add(s);
+                }
+            }
+
+            Map<String, String> expectedKeywords = new HashMap<String, String>();
+            if (testcase[2] != null) {
+                String[] ukeys = testcase[2].split(",");
+                for (int i = 0; i < ukeys.length; i++) {
+                    expectedKeywords.put(ukeys[i], testcase[i + 3]);
+                }
+            }
+
+            // Check attributes
+            Set<String> attributes = loc.getUnicodeLocaleAttributes();
+            if (attributes.size() != expectedAttributes.size()) {
+                errln("Incorrect number for Unicode locale attributes: returned=" 
+                        + attributes.size() + ", expected=" + expectedAttributes.size()
+                        + ", locale=" + testcase[0]);
+            }
+            if (!attributes.containsAll(expectedAttributes) || !expectedAttributes.containsAll(attributes)) {
+                errln("Incorrect set of attributes for locale " + testcase[0]);
+            }
+
+            // Check keywords
+            Set<String> keys = loc.getUnicodeLocaleKeys();
+            Set<String> expectedKeys = expectedKeywords.keySet();
+            if (keys.size() != expectedKeys.size()) {
+                errln("Incorrect number for Unicode locale keys: returned=" 
+                        + keys.size() + ", expected=" + expectedKeys.size()
+                        + ", locale=" + testcase[0]);
+            }
+
+            for (String expKey : expectedKeys) {
+                String type = loc.getUnicodeLocaleType(expKey);
+                String expType = expectedKeywords.get(expKey);
+
+                if (type == null || !expType.equals(type)) {
+                    errln("Incorrect Unicode locale type: key=" 
+                            + expKey + ", returned=" + type + ", expected=" + expType
+                            + ", locale=" + testcase[0]);
+                }
+            }
+        }
+
+        // Exception handling
+        boolean sawException = false;
+        try {
+            ULocale l = ULocale.forLanguageTag("en-US-u-ca-gregory");
+            l.getUnicodeLocaleType("$%");
+        } catch (IllegalArgumentException e) {
+            sawException = true;
+        }
+        if (!sawException) {
+            errln("getUnicodeLocaleType must throw an exception on illegal input key");
+        }
+    }
+
+    public void TestForLocale() {
+        Object[][] DATA = {
+            {new Locale(""),                    ""},
+            {new Locale("en", "US"),            "en_US"},
+            {new Locale("en", "US", "POSIX"),   "en_US_POSIX"},
+            {new Locale("", "US"),              "_US"},
+            {new Locale("en", "", "POSIX"),     "en__POSIX"},
+            {new Locale("no", "NO", "NY"),      "nn_NO"},
+            {new Locale("en", "BOGUS"),         "en__BOGUS"}, // ill-formed country is mapped to variant - see #8383 and #8384
+        };
+
+        for (int i = 0; i < DATA.length; i++) {
+            ULocale uloc = ULocale.forLocale((Locale) DATA[i][0]);
+            assertEquals("forLocale with " + DATA[i][0], DATA[i][1], uloc.getName());
+        }
+
+        if (JAVA7_OR_LATER) {
+            Object[][] DATA7 = {
+                {new Locale("ja", "JP", "JP"),      "ja_JP_JP@calendar=japanese"},
+                {new Locale("th", "TH", "TH"),      "th_TH_TH@numbers=thai"},
+            };
+            for (int i = 0; i < DATA7.length; i++) {
+                ULocale uloc = ULocale.forLocale((Locale) DATA7[i][0]);
+                assertEquals("forLocale with " + DATA7[i][0], DATA7[i][1], uloc.getName());
+            }
+
+            try {
+                Method localeForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
+
+                String[][] DATA7EXT = {
+                    {"en-Latn-US",                  "en_Latn_US"},
+                    {"zh-Hant-TW",                  "zh_Hant_TW"},
+                    {"und-US-u-cu-usd",             "_US@currency=usd"},
+                    {"th-TH-u-ca-buddhist-nu-thai", "th_TH@calendar=buddhist;numbers=thai"},
+                    {"en-US-u-va-POSIX",            "en_US_POSIX"},
+                    {"de-DE-u-co-phonebk",          "de_DE@collation=phonebook"},
+                    {"en-a-exta-b-extb-x-privu",    "en@a=exta;b=extb;x=privu"},
+                    {"fr-u-attr1-attr2-cu-eur",     "fr@attribute=attr1-attr2;currency=eur"},
+                };
+
+                for (int i = 0; i < DATA7EXT.length; i++) {
+                    Locale loc = (Locale) localeForLanguageTag.invoke(null, DATA7EXT[i][0]);
+                    ULocale uloc = ULocale.forLocale(loc);
+                    assertEquals("forLocale with " + loc, DATA7EXT[i][1], uloc.getName());
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+        } else {
+            Object[][] DATA6 = {
+                {new Locale("ja", "JP", "JP"),      "ja_JP@calendar=japanese"},
+                {new Locale("th", "TH", "TH"),      "th_TH@numbers=thai"},
+            };
+            for (int i = 0; i < DATA6.length; i++) {
+                ULocale uloc = ULocale.forLocale((Locale) DATA6[i][0]);
+                assertEquals("forLocale with " + DATA6[i][0], DATA6[i][1], uloc.getName());
+            }
+        }
+    }
+
+    public void TestToLocale() {
+        Object[][] DATA = {
+            {"",                new Locale("")},
+            {"en_US",           new Locale("en", "US")},
+            {"_US",             new Locale("", "US")},
+            {"en__POSIX",       new Locale("en", "", "POSIX")},
+        };
+
+        for (int i = 0; i < DATA.length; i++) {
+            Locale loc = new ULocale((String) DATA[i][0]).toLocale();
+            assertEquals("toLocale with " + DATA[i][0], DATA[i][1], loc);
+        }
+
+        if (JAVA7_OR_LATER) {
+            Object[][] DATA7 = {
+                    {"nn_NO",                       new Locale("nn", "NO")},
+                    {"no_NO_NY",                    new Locale("no", "NO", "NY")},
+            };
+            for (int i = 0; i < DATA7.length; i++) {
+                Locale loc = new ULocale((String) DATA7[i][0]).toLocale();
+                assertEquals("toLocale with " + DATA7[i][0], DATA7[i][1], loc);
+            }
+
+            try {
+                Method localeForLanguageTag = Locale.class.getMethod("forLanguageTag", String.class);
+
+                String[][] DATA7EXT = {
+                    {"en_Latn_US",                  "en-Latn-US"},
+                    {"zh_Hant_TW",                  "zh-Hant-TW"},
+                    {"ja_JP@calendar=japanese",     "ja-JP-u-ca-japanese"},
+                    {"ja_JP_JP@calendar=japanese",  "ja-JP-u-ca-japanese-x-lvariant-JP"},
+                    {"th_TH@numbers=thai",          "th-TH-u-nu-thai"},
+                    {"th_TH_TH@numbers=thai",       "th-TH-u-nu-thai-x-lvariant-TH"},
+                    {"de@collation=phonebook",      "de-u-co-phonebk"},
+                    {"en@a=exta;b=extb;x=privu",    "en-a-exta-b-extb-x-privu"},
+                    {"fr@attribute=attr1-attr2;currency=eur",   "fr-u-attr1-attr2-cu-eur"},
+                };
+
+                for (int i = 0; i < DATA7EXT.length; i++) {
+                    Locale loc = new ULocale((String) DATA7EXT[i][0]).toLocale();
+                    Locale expected = (Locale) localeForLanguageTag.invoke(null, DATA7EXT[i][1]);
+                    assertEquals("toLocale with " + DATA7EXT[i][0], expected, loc);
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+        } else {
+            Object[][] DATA6 = {
+                {"nn_NO",                       new Locale("no", "NO", "NY")},
+                {"no_NO_NY",                    new Locale("no", "NO", "NY")},
+                {"ja_JP@calendar=japanese",     new Locale("ja", "JP", "JP")},
+                {"th_TH@numbers=thai",          new Locale("th", "TH", "TH")},
+            };
+            for (int i = 0; i < DATA6.length; i++) {
+                Locale loc = new ULocale((String) DATA6[i][0]).toLocale();
+                assertEquals("toLocale with " + DATA6[i][0], DATA6[i][1], loc);
+            }
+        }
+    }
 }
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
index 6d15f20..7d844b9 100644
--- 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
@@ -7,7 +7,7 @@
 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
diff --git a/main/tests/translit/src/com/ibm/icu/dev/test/translit/UnicodeMapTest.java b/main/tests/translit/src/com/ibm/icu/dev/test/translit/UnicodeMapTest.java
index 3fe4f20..fb159b2 100644
--- a/main/tests/translit/src/com/ibm/icu/dev/test/translit/UnicodeMapTest.java
+++ b/main/tests/translit/src/com/ibm/icu/dev/test/translit/UnicodeMapTest.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.                                                *
  *******************************************************************************
  */
@@ -9,12 +9,12 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.Map.Entry;
 
 import com.ibm.icu.dev.test.TestFmwk;
 import com.ibm.icu.dev.test.util.UnicodeMap;
diff --git a/main/tests/translit/src/com/ibm/icu/dev/test/util/PrettyPrinter.java b/main/tests/translit/src/com/ibm/icu/dev/test/util/PrettyPrinter.java
index f483da7..575775e 100644
--- a/main/tests/translit/src/com/ibm/icu/dev/test/util/PrettyPrinter.java
+++ b/main/tests/translit/src/com/ibm/icu/dev/test/util/PrettyPrinter.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.                                                *
  **********************************************************************
  * Author: Mark Davis
@@ -18,9 +18,9 @@
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.text.StringTransform;
 import com.ibm.icu.text.UTF16;
+import com.ibm.icu.text.UTF16.StringComparator;
 import com.ibm.icu.text.UnicodeSet;
 import com.ibm.icu.text.UnicodeSetIterator;
-import com.ibm.icu.text.UTF16.StringComparator;
 
 /** Provides more flexible formatting of UnicodeSet patterns.
  */
diff --git a/main/tests/translit/src/com/ibm/icu/dev/test/util/UnicodeMap.java b/main/tests/translit/src/com/ibm/icu/dev/test/util/UnicodeMap.java
index f3bd5ba..9ceeceb 100644
--- a/main/tests/translit/src/com/ibm/icu/dev/test/util/UnicodeMap.java
+++ b/main/tests/translit/src/com/ibm/icu/dev/test/util/UnicodeMap.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.                                                *
  *******************************************************************************
  */
@@ -14,10 +14,10 @@
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.Map.Entry;
 
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.StringTransform;
diff --git a/readme.html b/readme.html
index bdcd9cd..815d421 100644
--- a/readme.html
+++ b/readme.html
@@ -4,7 +4,7 @@
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <meta http-equiv="Content-Style-Type" content="text/css2">
   <title>ReadMe for ICU4J</title>
-  <meta name="COPYRIGHT" content="Copyright 2000-2010, International Business Machines Corporation and others. All Rights Reserved.">
+  <meta name="COPYRIGHT" content="Copyright 2000-2011, International Business Machines Corporation and others. All Rights Reserved.">
   <style type="text/css">
 h3.doc { background: #CCCCFF }
 h4.doc { text-decoration: underline }
@@ -13,11 +13,10 @@
 <body style="background-color: rgb(255, 255, 255);" lang="EN-US"
  link="#0000ff" vlink="#800080">
 <h2>International Components for Unicode for Java (ICU4J)</h2>
-<h3>Read Me for ICU4J 4.4.2</h3>
+<h3>Read Me for ICU4J 4.4.2.2</h3>
 <hr size="2" width="100%">
 <p><b>Release Date</b><br>
-October 1, 2010<br>
-</p>
+August 24, 2011</p>
 
 <p><b>Note:</b> This is an update release of ICU4J 4.4.  This release contains
 several bug fixes and updated data, but does not introduce any new APIs or
@@ -168,6 +167,54 @@
 <a href="http://source.icu-project.org/repos/icu/icu4j/tags/release-4-4/APIChangeReport.html">here</a>.
 </p>
 
+<h4 class="doc">Summary of updates in 4.4.2.2 (August 24, 2011)</h4>
+<p><b>Java 7 Locale support</b></p>
+<p>Java 7 was shipped in July 2011 including some enhancements in java.util.Locale class.
+For supporting the enhancements in Locale class, ICU4J com.ibm.icu.util.ULocale was also
+updated in the latest code stream by following tickets.
+<ul>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8078">#8078</a> ICU4J APIs taking Locale to use new JDK7 APIs to access script/extensions</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8630">#8630</a> Java 7 Locale Category support</li>
+</ul>
+These changes in ULocale class were back-ported to this release. (<b>Note</b>: The new APIs for locale category
+are tagged as @stable ICU 49, indicating these APIs are back-ported from later code stream.)</p>
+
+<p><b>Bug fixes</b></p>
+<p>This release contains following bug fixes
+<ul>
+<li><a href="http://bugs.icu-project.org/trac/ticket/6108">#6108</a> DecimalFormat fraction digit problem with rounding + BigDecimal</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8410">#8410</a> In IndianCalendar, the add operation performs like roll</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8484">#8484</a> endless loop in RuleBasedCollator.getSortKeyBytes</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8549">#8549</a> UTF-7 error handling consumes too many valid subsequent chars</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8569">#8569</a> UTF-7 missing terminating shift character</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8596">#8596</a> GregorianCalendar.getActualMaximum(Calendar.WEEK_OF_YEAR) returns 52 when it should return 53</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8624">#8624</a> ucol_getSortKey+strncmp and ucol_strcoll give different comparison result</li>
+</ul>
+</p>
+
+<h4 class="doc">Summary of updates in 4.4.2.1 (July 1, 2011)</h4>
+<p><b>ICU4J plug-in for Eclipse 3.7</b></p>
+<p>ICU4J 4.4.2.1 source archive includes files for building ICU4J plug-in for Eclipse 3.7.</p>
+
+<p><b>Tme zone data update</b></p>
+<p>ICU4J 4.4.2.1 updates the time zone data to version
+<a href="http://source.icu-project.org/repos/icu/data/trunk/tzdata/icunew/2011h/44/">2011h</a>
+from the Olson tz database.</p>
+
+<p><b>Bug fix</b></p>
+<p>This release contains following bug fix
+<ul>
+<li><a href="http://bugs.icu-project.org/trac/ticket/7201">#7201</a> Transliterator is not thread safe</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/7480">#7480</a> Wrong year from SimpleDateFormat.parse()</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/7690">#7690</a> OlsonTimeZone.hashCode() throws NullPointerException</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/7880">#7880</a> Race in VersionInfo.getInstance()</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8275">#8275</a> ICU4J Locale service returns incorrect result with unsupported class loader protocol</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8283">#8283</a> ICU4J MessageFormat should use varargs in format method</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8258">#8258</a> Avoid UnicodeSet constructors taking String pattern when all code points are known</li>
+<li><a href="http://bugs.icu-project.org/trac/ticket/8259">#8259</a> Use Java StringTokenizer instead of ICU in ICUResourceBundle and VTimeZone</li>
+</ul>
+</p>
+
 <h4 class="doc">Summary of updates in 4.4.2 (October 1, 2010)</h4>
 <p><b>Tme zone data update</b></p>
 <p>ICU4J 4.4.2 updates the time zone data to version
@@ -1093,8 +1140,8 @@
     the CLDR source files checked out.</li>
     <li>Update <I>$cldr_root</I>/common to 'release-1-8-1' tag</li>
     <li>Update <I>$cldr_root</I>/tools to 'release-1-8-1' tag</li>
-    <li>Checkout ICU4C with tag 'release-4-4-1'</li>
-    <li>Checkout ICU4J with tag 'release-4-4-1'</li>
+    <li>Checkout ICU4C with tag 'release-4-4-2'</li>
+    <li>Checkout ICU4J with tag 'release-4-4-2-2'</li>
     <li>Build ICU4J</li>
     <li>Build ICU4C</li>
     <li>Change to <I>$cldr_root</I>/tools/java directory</li>
@@ -1108,7 +1155,7 @@
 </ol>
 
 <h3 class="doc"><a name="timezone"></a>About ICU4J Time Zone</h3>
-<p>ICU4J 4.4.2 includes time zone data version 2010m, which is the latest one as of
+<p>ICU4J 4.4.2.2 includes time zone data version 2011h, which is the latest one as of
 the release date.  However, time zone data is frequently updated in response
 to changes made by local governments around the world.  If you need to update
 the time zone data, please refer the ICU user guide topic
@@ -1146,7 +1193,7 @@
 <h2>Thank you for your interest in ICU4J!</h2>
 <br>
 <hr align="center" size="2" width="100%">
-<p><I><font size="-1">Copyright &copy; 2002-2010 International Business
+<p><I><font size="-1">Copyright &copy; 2002-2011 International Business
 Machines Corporation and others. All Rights
 Reserved.<br>
 4400 North First Street, San Jos&eacute;, CA 95193, USA