src/java.base/share/classes/sun/util/locale/BaseLocale.java
changeset 48763 3d17a524da95
parent 47216 71c04702a3d5
child 54412 41356f083e93
equal deleted inserted replaced
48762:85c27aabf312 48763:3d17a524da95
     1 /*
     1 /*
     2  * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    29  * others. All Rights Reserved.                                                *
    29  * others. All Rights Reserved.                                                *
    30  *******************************************************************************
    30  *******************************************************************************
    31  */
    31  */
    32 
    32 
    33 package sun.util.locale;
    33 package sun.util.locale;
       
    34 
    34 import java.lang.ref.SoftReference;
    35 import java.lang.ref.SoftReference;
    35 
       
    36 import java.util.StringJoiner;
    36 import java.util.StringJoiner;
    37 
    37 
    38 public final class BaseLocale {
    38 public final class BaseLocale {
    39 
    39 
    40     public static final String SEP = "_";
    40     public static final String SEP = "_";
    46     private final String region;
    46     private final String region;
    47     private final String variant;
    47     private final String variant;
    48 
    48 
    49     private volatile int hash;
    49     private volatile int hash;
    50 
    50 
    51     // This method must be called only when creating the Locale.* constants.
    51     // This method must be called with normalize = false only when creating the
    52     private BaseLocale(String language, String region) {
    52     // Locale.* constants and non-normalized BaseLocale$Keys used for lookup.
    53         this.language = language;
    53     private BaseLocale(String language, String script, String region, String variant,
    54         this.script = "";
    54                        boolean normalize) {
    55         this.region = region;
    55         if (normalize) {
    56         this.variant = "";
    56             this.language = LocaleUtils.toLowerString(language).intern();
    57     }
    57             this.script = LocaleUtils.toTitleString(script).intern();
    58 
    58             this.region = LocaleUtils.toUpperString(region).intern();
    59     private BaseLocale(String language, String script, String region, String variant) {
    59             this.variant = variant.intern();
    60         this.language = (language != null) ? LocaleUtils.toLowerString(language).intern() : "";
    60         } else {
    61         this.script = (script != null) ? LocaleUtils.toTitleString(script).intern() : "";
    61             this.language = language;
    62         this.region = (region != null) ? LocaleUtils.toUpperString(region).intern() : "";
    62             this.script = script;
    63         this.variant = (variant != null) ? variant.intern() : "";
    63             this.region = region;
       
    64             this.variant = variant;
       
    65         }
    64     }
    66     }
    65 
    67 
    66     // Called for creating the Locale.* constants. No argument
    68     // Called for creating the Locale.* constants. No argument
    67     // validation is performed.
    69     // validation is performed.
    68     public static BaseLocale createInstance(String language, String region) {
    70     public static BaseLocale createInstance(String language, String region) {
    69         BaseLocale base = new BaseLocale(language, region);
    71         BaseLocale base = new BaseLocale(language, "", region, "", false);
    70         CACHE.put(new Key(language, region), base);
    72         CACHE.put(new Key(base), base);
    71         return base;
    73         return base;
    72     }
    74     }
    73 
    75 
    74     public static BaseLocale getInstance(String language, String script,
    76     public static BaseLocale getInstance(String language, String script,
    75                                          String region, String variant) {
    77                                          String region, String variant) {
    82             } else if (LocaleUtils.caseIgnoreMatch(language, "id")) {
    84             } else if (LocaleUtils.caseIgnoreMatch(language, "id")) {
    83                 language = "in";
    85                 language = "in";
    84             }
    86             }
    85         }
    87         }
    86 
    88 
    87         Key key = new Key(language, script, region, variant);
    89         Key key = new Key(language, script, region, variant, false);
    88         BaseLocale baseLocale = CACHE.get(key);
    90         BaseLocale baseLocale = CACHE.get(key);
    89         return baseLocale;
    91         return baseLocale;
    90     }
    92     }
    91 
    93 
    92     public String getLanguage() {
    94     public String getLanguage() {
   121     }
   123     }
   122 
   124 
   123     @Override
   125     @Override
   124     public String toString() {
   126     public String toString() {
   125         StringJoiner sj = new StringJoiner(", ");
   127         StringJoiner sj = new StringJoiner(", ");
   126         if (language.length() > 0) {
   128         if (!language.isEmpty()) {
   127             sj.add("language=" + language);
   129             sj.add("language=" + language);
   128         }
   130         }
   129         if (script.length() > 0) {
   131         if (!script.isEmpty()) {
   130             sj.add("script=" + script);
   132             sj.add("script=" + script);
   131         }
   133         }
   132         if (region.length() > 0) {
   134         if (!region.isEmpty()) {
   133             sj.add("region=" + region);
   135             sj.add("region=" + region);
   134         }
   136         }
   135         if (variant.length() > 0) {
   137         if (!variant.isEmpty()) {
   136             sj.add("variant=" + variant);
   138             sj.add("variant=" + variant);
   137         }
   139         }
   138         return sj.toString();
   140         return sj.toString();
   139     }
   141     }
   140 
   142 
   153         }
   155         }
   154         return h;
   156         return h;
   155     }
   157     }
   156 
   158 
   157     private static final class Key {
   159     private static final class Key {
   158         private final SoftReference<String> lang;
   160         /**
   159         private final SoftReference<String> scrt;
   161          * Keep a SoftReference to the Key data if normalized (actually used
   160         private final SoftReference<String> regn;
   162          * as a cache key) and not initialized via the constant creation path.
   161         private final SoftReference<String> vart;
   163          *
       
   164          * This allows us to avoid creating SoftReferences on lookup Keys
       
   165          * (which are short-lived) and for Locales created via
       
   166          * Locale#createConstant.
       
   167          */
       
   168         private final SoftReference<BaseLocale> holderRef;
       
   169         private final BaseLocale holder;
       
   170 
   162         private final boolean normalized;
   171         private final boolean normalized;
   163         private final int hash;
   172         private final int hash;
   164 
   173 
   165         /**
   174         /**
   166          * Creates a Key. language and region must be normalized
   175          * Creates a Key. language and region must be normalized
   167          * (intern'ed in the proper case).
   176          * (intern'ed in the proper case).
   168          */
   177          */
   169         private Key(String language, String region) {
   178         private Key(BaseLocale locale) {
   170             assert language.intern() == language
   179             this.holder = locale;
   171                    && region.intern() == region;
   180             this.holderRef = null;
   172 
       
   173             lang = new SoftReference<>(language);
       
   174             scrt = new SoftReference<>("");
       
   175             regn = new SoftReference<>(region);
       
   176             vart = new SoftReference<>("");
       
   177             this.normalized = true;
   181             this.normalized = true;
       
   182             String language = locale.getLanguage();
       
   183             String region = locale.getRegion();
       
   184             assert LocaleUtils.toLowerString(language).intern() == language
       
   185                     && LocaleUtils.toUpperString(region).intern() == region
       
   186                     && locale.getVariant() == ""
       
   187                     && locale.getScript() == "";
   178 
   188 
   179             int h = language.hashCode();
   189             int h = language.hashCode();
   180             if (region != "") {
   190             if (region != "") {
   181                 int len = region.length();
   191                 int len = region.length();
   182                 for (int i = 0; i < len; i++) {
   192                 for (int i = 0; i < len; i++) {
   184                 }
   194                 }
   185             }
   195             }
   186             hash = h;
   196             hash = h;
   187         }
   197         }
   188 
   198 
   189         public Key(String language, String script, String region, String variant) {
       
   190             this(language, script, region, variant, false);
       
   191         }
       
   192 
       
   193         private Key(String language, String script, String region,
   199         private Key(String language, String script, String region,
   194                     String variant, boolean normalized) {
   200                     String variant, boolean normalize) {
       
   201             if (language == null) {
       
   202                 language = "";
       
   203             }
       
   204             if (script == null) {
       
   205                 script = "";
       
   206             }
       
   207             if (region == null) {
       
   208                 region = "";
       
   209             }
       
   210             if (variant == null) {
       
   211                 variant = "";
       
   212             }
       
   213 
       
   214             BaseLocale locale = new BaseLocale(language, script, region, variant, normalize);
       
   215             this.normalized = normalize;
       
   216             if (normalized) {
       
   217                 this.holderRef = new SoftReference<>(locale);
       
   218                 this.holder = null;
       
   219             } else {
       
   220                 this.holderRef = null;
       
   221                 this.holder = locale;
       
   222             }
       
   223             this.hash = hashCode(locale);
       
   224         }
       
   225 
       
   226         public int hashCode() {
       
   227             return hash;
       
   228         }
       
   229 
       
   230         private int hashCode(BaseLocale locale) {
   195             int h = 0;
   231             int h = 0;
   196             if (language != null) {
   232             String lang = locale.getLanguage();
   197                 lang = new SoftReference<>(language);
   233             int len = lang.length();
   198                 int len = language.length();
   234             for (int i = 0; i < len; i++) {
   199                 for (int i = 0; i < len; i++) {
   235                 h = 31*h + LocaleUtils.toLower(lang.charAt(i));
   200                     h = 31*h + LocaleUtils.toLower(language.charAt(i));
   236             }
   201                 }
   237             String scrt = locale.getScript();
   202             } else {
   238             len = scrt.length();
   203                 lang = new SoftReference<>("");
   239             for (int i = 0; i < len; i++) {
   204             }
   240                 h = 31*h + LocaleUtils.toLower(scrt.charAt(i));
   205             if (script != null) {
   241             }
   206                 scrt = new SoftReference<>(script);
   242             String regn = locale.getRegion();
   207                 int len = script.length();
   243             len = regn.length();
   208                 for (int i = 0; i < len; i++) {
   244             for (int i = 0; i < len; i++) {
   209                     h = 31*h + LocaleUtils.toLower(script.charAt(i));
   245                 h = 31*h + LocaleUtils.toLower(regn.charAt(i));
   210                 }
   246             }
   211             } else {
   247             String vart = locale.getVariant();
   212                 scrt = new SoftReference<>("");
   248             len = vart.length();
   213             }
   249             for (int i = 0; i < len; i++) {
   214             if (region != null) {
   250                 h = 31*h + vart.charAt(i);
   215                 regn = new SoftReference<>(region);
   251             }
   216                 int len = region.length();
   252             return h;
   217                 for (int i = 0; i < len; i++) {
   253         }
   218                     h = 31*h + LocaleUtils.toLower(region.charAt(i));
   254 
   219                 }
   255         private BaseLocale getBaseLocale() {
   220             } else {
   256             return (holder == null) ? holderRef.get() : holder;
   221                 regn = new SoftReference<>("");
       
   222             }
       
   223             if (variant != null) {
       
   224                 vart = new SoftReference<>(variant);
       
   225                 int len = variant.length();
       
   226                 for (int i = 0; i < len; i++) {
       
   227                     h = 31*h + variant.charAt(i);
       
   228                 }
       
   229             } else {
       
   230                 vart = new SoftReference<>("");
       
   231             }
       
   232             hash = h;
       
   233             this.normalized = normalized;
       
   234         }
   257         }
   235 
   258 
   236         @Override
   259         @Override
   237         public boolean equals(Object obj) {
   260         public boolean equals(Object obj) {
   238             if (this == obj) {
   261             if (this == obj) {
   239                 return true;
   262                 return true;
   240             }
   263             }
   241 
       
   242             if (obj instanceof Key && this.hash == ((Key)obj).hash) {
   264             if (obj instanceof Key && this.hash == ((Key)obj).hash) {
   243                 String tl = this.lang.get();
   265                 BaseLocale other = ((Key) obj).getBaseLocale();
   244                 String ol = ((Key)obj).lang.get();
   266                 BaseLocale locale = this.getBaseLocale();
   245                 if (tl != null && ol != null &&
   267                 if (other != null && locale != null
   246                     LocaleUtils.caseIgnoreMatch(ol, tl)) {
   268                     && LocaleUtils.caseIgnoreMatch(other.getLanguage(), locale.getLanguage())
   247                     String ts = this.scrt.get();
   269                     && LocaleUtils.caseIgnoreMatch(other.getScript(), locale.getScript())
   248                     String os = ((Key)obj).scrt.get();
   270                     && LocaleUtils.caseIgnoreMatch(other.getRegion(), locale.getRegion())
   249                     if (ts != null && os != null &&
   271                     // variant is case sensitive in JDK!
   250                         LocaleUtils.caseIgnoreMatch(os, ts)) {
   272                     && other.getVariant().equals(locale.getVariant())) {
   251                         String tr = this.regn.get();
   273                     return true;
   252                         String or = ((Key)obj).regn.get();
       
   253                         if (tr != null && or != null &&
       
   254                             LocaleUtils.caseIgnoreMatch(or, tr)) {
       
   255                             String tv = this.vart.get();
       
   256                             String ov = ((Key)obj).vart.get();
       
   257                             return (ov != null && ov.equals(tv));
       
   258                         }
       
   259                     }
       
   260                 }
   274                 }
   261             }
   275             }
   262             return false;
   276             return false;
   263         }
       
   264 
       
   265         @Override
       
   266         public int hashCode() {
       
   267             return hash;
       
   268         }
   277         }
   269 
   278 
   270         public static Key normalize(Key key) {
   279         public static Key normalize(Key key) {
   271             if (key.normalized) {
   280             if (key.normalized) {
   272                 return key;
   281                 return key;
   273             }
   282             }
   274 
   283 
   275             String lang = LocaleUtils.toLowerString(key.lang.get()).intern();
   284             // Only normalized keys may be softly referencing the data holder
   276             String scrt = LocaleUtils.toTitleString(key.scrt.get()).intern();
   285             assert (key.holder != null && key.holderRef == null);
   277             String regn = LocaleUtils.toUpperString(key.regn.get()).intern();
   286             BaseLocale locale = key.holder;
   278             String vart = key.vart.get().intern(); // preserve upper/lower cases
   287             return new Key(locale.getLanguage(), locale.getScript(),
   279 
   288                     locale.getRegion(), locale.getVariant(), true);
   280             return new Key(lang, scrt, regn, vart, true);
       
   281         }
   289         }
   282     }
   290     }
   283 
   291 
   284     private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
   292     private static class Cache extends LocaleObjectCache<Key, BaseLocale> {
   285 
   293 
   286         public Cache() {
   294         public Cache() {
   287         }
   295         }
   288 
   296 
   289         @Override
   297         @Override
   290         protected Key normalizeKey(Key key) {
   298         protected Key normalizeKey(Key key) {
   291             assert key.lang.get() != null &&
       
   292                    key.scrt.get() != null &&
       
   293                    key.regn.get() != null &&
       
   294                    key.vart.get() != null;
       
   295 
       
   296             return Key.normalize(key);
   299             return Key.normalize(key);
   297         }
   300         }
   298 
   301 
   299         @Override
   302         @Override
   300         protected BaseLocale createObject(Key key) {
   303         protected BaseLocale createObject(Key key) {
   301             return new BaseLocale(key.lang.get(), key.scrt.get(),
   304             return Key.normalize(key).getBaseLocale();
   302                                   key.regn.get(), key.vart.get());
       
   303         }
   305         }
   304     }
   306     }
   305 }
   307 }