Package com.google.gwt.i18n.server

Source Code of com.google.gwt.i18n.server.GwtLocaleImpl

/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.i18n.server;

import com.google.gwt.i18n.shared.GwtLocale;
import com.google.gwt.i18n.shared.GwtLocaleFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Class representing GWT locales and conversion to/from other formats.
*
* These locales correspond to BCP47.
*/
public class GwtLocaleImpl implements GwtLocale {
  // TODO(jat): implement a version of this suitable for client-side use,
  // probably using a generator to include only the information relevant to
  // the set of locales supported, and then figure out a way to get that into
  // the property provider to handle inheritance there.

  /**
   * Maps deprecated language codes to the canonical code.  Strings are always
   * in pairs, with the first being the canonical code and the second being
   * a deprecated code which maps to it.
   *
   * Source: http://www.loc.gov/standards/iso639-2/php/code_changes.php
   *
   * TODO: consider building maps if this list grows much.
   */
  private static final String[] deprecatedLanguages = new String[] {
    "he", "iw",   // Hebrew
    "id", "in",   // Indonesian
    "jv", "jw",   // Javanese, typo in original publication
    "ro", "mo",   // Moldovian
    "yi", "ji",   // Yiddish
  };

  /**
   * Maps deprecated region codes to the canonical code.  Strings are always
   * in pairs, with the first being the canonical code and the second being
   * a deprecated code which maps to it.
   *
   * Note that any mappings which split an old code into multiple new codes
   * cannot be done automatically (such as cs -> rs/me) -- perhaps we could
   * have a way of flagging region codes which are no longer valid and allow
   * an appropriate warning message.
   *
   * Source: http://en.wikipedia.org/wiki/ISO_3166-1
   *
   * TODO: consider building maps if this list grows much.
   */
  private static final String[] deprecatedRegions = new String[] {
    // East Timor - http://www.iso.org/iso/newsletter_v-5_east_timor.pdf
    "TL", "TP",
  };

  /**
   * Add in the missing locale of a deprecated pair.
   *
   * @param factory GwtLocaleFactory to create new instances with
   * @param locale locale to add deprecated pair for
   * @param aliases where to store the alias if present
   */
  private static void addDeprecatedPairs(GwtLocaleFactory factory,
      GwtLocale locale, List<GwtLocale> aliases) {
    int n = deprecatedLanguages.length;
    for (int i = 0; i < n; i += 2) {
      if (deprecatedLanguages[i].equals(locale.getLanguage())) {
        aliases.add(factory.fromComponents(deprecatedLanguages[i + 1],
            locale.getScript(), locale.getRegion(), locale.getVariant()));
        break;
      }
      if (deprecatedLanguages[i + 1].equals(locale.getLanguage())) {
        aliases.add(factory.fromComponents(deprecatedLanguages[i],
            locale.getScript(), locale.getRegion(), locale.getVariant()));
        break;
      }
    }
  }

  private static void addImmediateParentRegions(GwtLocaleFactory factory,
      GwtLocale locale, Collection<GwtLocale> work) {
    String language = locale.getLanguage();
    String script = locale.getScript();
    String region = locale.getRegion();
    String variant = locale.getVariant();
    if (variant != null) {
      work.add(factory.fromComponents(language, script, region, null));
    }
    Set<String> immediateParents = RegionInheritance.getImmediateParents(region);
    for (String parent : immediateParents) {
      work.add(factory.fromComponents(language, script, parent, variant));
      if (variant != null) {
        work.add(factory.fromComponents(language, script, parent, null));
      }
    }
    if (immediateParents.isEmpty()) {
      work.add(factory.fromComponents(language, script, null, variant));
      if (variant != null) {
        work.add(factory.fromComponents(language, script, null, null));
      }
    }
    if (script != null) {
      work.add(factory.fromComponents(language, null, region, variant));
      if (variant != null) {
        work.add(factory.fromComponents(language, null, region, null));
      }
    }
  }

  /**
   * Add inherited regions for a given locale.
   *
   * @param factory
   * @param inherits
   * @param language
   * @param script
   * @param region
   * @param variant
   */
  private static void addParentRegionLocales(GwtLocaleFactory factory,
      List<GwtLocale> inherits, String language, String script, String region,
      String variant) {
    for (String parent : RegionInheritance.getAllAncestors(region)) {
      inherits.add(factory.fromComponents(language, script, parent, variant));
    }
  }

  /**
   * Add special aliases for a given locale.
   *
   * This includes things like choosing the default script/region for Chinese
   * based on the other one, handling Norwegian language changes, and treating
   * pt_BR as the default pt type.
   *
   * @param factory GwtLocaleFactory to create new instances with
   * @param locale locale to add deprecated pair for
   * @param aliases where to store the alias if present
   */
  private static void addSpecialAliases(GwtLocaleFactory factory,
      GwtLocale locale, List<GwtLocale> aliases) {
    String language = locale.getLanguage();
    String script = locale.getScript();
    String region = locale.getRegion();
    String variant = locale.getVariant();
    if ("zh".equals(language)) {
      if (script == null) {
        // infer script from region
        if ("TW".equals(region)) {
          aliases.add(factory.fromComponents("zh", "Hant", region, variant));
        } else if ("CN".equals(region)) {
          aliases.add(factory.fromComponents("zh", "Hans", region, variant));
        }
      } else {
        if (region == null) {
          // Add aliases for main users of particular scripts
          aliases.add(factory.fromComponents("zh", script,
              "Hant".equals(script) ? "TW" : "CN", variant));
        }
      }
    } else if ("no".equals(language)) {
      if (variant == null || "BOKMAL".equals(variant)) {
        aliases.add(factory.fromComponents("nb", script, region, null));
        aliases.add(factory.fromComponents("no-bok", script, region, null));
      } else if ("NYNORSK".equals(variant)) {
        aliases.add(factory.fromComponents("nn", script, region, null));
        aliases.add(factory.fromComponents("no-nyn", script, region, null));
      }
    } else if ("nb".equals(language)) {
      aliases.add(factory.fromComponents("no", script, region, "BOKMAL"));
    } else if ("nn".equals(language)) {
      aliases.add(factory.fromComponents("no", script, region, "NYNORSK"));
    } else if ("pt".equals(language)) {
      if (region == null) {
        aliases.add(factory.fromComponents("pt", script, "BR", variant));
      } else if ("BR".equals(region)) {
        aliases.add(factory.fromComponents("pt", script, null, variant));
      }
    }
  }

  private static boolean equalsNullCheck(String str1, String str2) {
    if (str1 == null) {
      return str2 == null;
    }
    return str1.equals(str2);
  }

  /**
   * Compare strings, accounting for nulls (which are treated as before any
   * other value).
   *
   * @param a first string
   * @param b second string
   *
   * @return positive if a>b, negative if a<b, 0 if equal
   */
  private static int stringCompare(String a, String b) {
    if (a == null) {
      return b == null ? 0 : -1;
    }
    if (b == null) {
      return 1;
    }
    return a.compareTo(b);
  }

  private final GwtLocaleFactory factory;

  private final String language;

  private final String region;

  private final String script;

  private final String variant;

  private ArrayList<GwtLocale> cachedSearchList;

  private ArrayList<GwtLocale> cachedAliases;

  /**
   * Must only be called from a factory to preserve instance caching.
   */
  GwtLocaleImpl(GwtLocaleFactory factory, String language, String region,
      String script, String variant) {
    this.factory = factory;
    this.language = language;
    this.region = region;
    this.script = script;
    this.variant = variant;
  }

  public int compareTo(GwtLocale o) {
    int c = stringCompare(language, o.getLanguage());
    if (c == 0) {
      c = stringCompare(script, o.getScript());
    }
    if (c == 0) {
      c = stringCompare(region, o.getRegion());
    }
    if (c == 0) {
      c = stringCompare(variant, o.getVariant());
    }
    return c;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof GwtLocale)) {
      return false;
    }
    GwtLocale other = (GwtLocale) obj;
    return equalsNullCheck(language, other.getLanguage())
        && equalsNullCheck(region, other.getRegion())
        && equalsNullCheck(script, other.getScript())
        && equalsNullCheck(variant, other.getVariant());
  }

  public List<GwtLocale> getAliases() {
    // TODO(jat): more locale aliases? better way to encode them?
    if (cachedAliases == null) {
      cachedAliases = new ArrayList<GwtLocale>();
      GwtLocale canonicalForm = getCanonicalForm();
      Set<GwtLocale> seen = new HashSet<GwtLocale>();
      cachedAliases.add(canonicalForm);
      ArrayList<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
      nextGroup.add(this);
      // Account for default script
      String defaultScript = DefaultLanguageScripts.getDefaultScript(language);
      if (defaultScript != null) {
        if (script == null) {
          nextGroup.add(factory.fromComponents(language, defaultScript, region,
              variant));
        } else if (script.equals(defaultScript)) {
          nextGroup.add(factory.fromComponents(language, null, region, variant));
        }
      }
      while (!nextGroup.isEmpty()) {
        List<GwtLocale> thisGroup = nextGroup;
        nextGroup = new ArrayList<GwtLocale>();
        for (GwtLocale locale : thisGroup) {
          if (seen.contains(locale)) {
            continue;
          }
          seen.add(locale);
          if (!locale.equals(canonicalForm)) {
            cachedAliases.add(locale);
          }
          addDeprecatedPairs(factory, locale, nextGroup);
          addSpecialAliases(factory, locale, nextGroup);
        }
      }
    }
    return Collections.unmodifiableList(cachedAliases);
  }

  public String getAsString() {
    StringBuilder buf = new StringBuilder();
    if (language != null) {
      buf.append(language);
    }
    if (script != null) {
      buf.append('_');
      buf.append(script);
    }
    if (region != null) {
      buf.append('_');
      buf.append(region);
    }
    if (variant != null) {
      buf.append('_');
      buf.append(variant);
    }
    return buf.toString();
  }

  /**
   * Returns this locale in canonical form.  Changes for canonical form are:
   * <ul>
   <li>Deprecated language/region tags are replaced with official versions
   * </ul>
   *
   * @return GwtLocale instance
   */
  public GwtLocale getCanonicalForm() {
    String canonLanguage = language;
    String canonScript = script;
    String canonRegion = region;
    String canonVariant = variant;
    // Handle deprecated language codes
    int n = deprecatedLanguages.length;
    for (int i = 0; i < n; i += 2) {
      if (deprecatedLanguages[i + 1].equals(canonLanguage)) {
        canonLanguage = deprecatedLanguages[i];
        break;
      }
    }
    // Handle deprecated region codes
    n = deprecatedRegions.length;
    for (int i = 0; i < n; i += 2) {
      if (deprecatedRegions[i + 1].equals(canonRegion)) {
        canonRegion = deprecatedRegions[i];
        break;
      }
    }
    // Special-case Chinese default scripts
    if ("zh".equals(canonLanguage)) {
      if (canonRegion != null) {
        if ("CN".equals(canonRegion) && "Hans".equals(canonScript)) {
          canonScript = null;
        } else if ("TW".equals(canonRegion) && "Hant".equals(canonScript)) {
          canonScript = null;
        }
      } else if ("Hans".equals(canonScript)) {
        canonRegion = "CN";
        canonScript = null;
      } else if ("Hant".equals(canonScript)) {
        canonRegion = "TW";
        canonScript = null;
      }
    }
    // Special-case no->nb/nn split
    if ("no-bok".equals(canonLanguage)) {
      canonLanguage = "nb";
      canonVariant = null;
    } else if ("no-nyn".equals(canonLanguage)) {
      canonLanguage = "nn";
      canonVariant = null;
    } else if ("no".equals(canonLanguage)) {
      if (canonVariant == null || "BOKMAL".equals(canonVariant)) {
        canonLanguage = "nb";
        canonVariant = null;
      } else if ("NYNORSK".equals(canonVariant)) {
        canonLanguage = "nn";
        canonVariant = null;
      }
    }
    // Remove any default script for the language
    if (canonScript != null && canonScript.equals(
        DefaultLanguageScripts.getDefaultScript(canonLanguage))) {
      canonScript = null;
    }
    return factory.fromComponents(canonLanguage, canonScript, canonRegion,
        canonVariant);
  }

  public List<GwtLocale> getCompleteSearchList() {
    // TODO(jat): get zh_Hant to come before zh in search list for zh_TW
    if (cachedSearchList == null) {
      cachedSearchList = new ArrayList<GwtLocale>();
      Set<GwtLocale> seen = new HashSet<GwtLocale>();
      List<GwtLocale> thisGroup = new ArrayList<GwtLocale>(this.getAliases());
      seen.addAll(thisGroup);
      GwtLocale defLocale = factory.getDefault();
      seen.add(defLocale);
      while (!thisGroup.isEmpty()) {
        cachedSearchList.addAll(thisGroup);
        List<GwtLocale> nextGroup = new ArrayList<GwtLocale>();
        for (GwtLocale locale : thisGroup) {
          List<GwtLocale> work = new ArrayList<GwtLocale>(locale.getAliases());
          work.removeAll(seen);
          nextGroup.addAll(work);
          seen.addAll(work);
          work.clear();
          if (locale.getRegion() != null) {
            addImmediateParentRegions(factory, locale, work);
          } else if (locale.getVariant() != null) {
            work.add(factory.fromComponents(locale.getLanguage(),
                locale.getScript(), null, null));
          } else if (locale.getScript() != null) {
            work.add(factory.fromComponents(locale.getLanguage(), null, null,
                null));
          }
          work.removeAll(seen);
          nextGroup.addAll(work);
          seen.addAll(work);
        }
        thisGroup = nextGroup;
      }
      if (!isDefault()) {
        cachedSearchList.add(defLocale);
      }
    }
    return cachedSearchList;
  }

  /**
   * Return a list of locales to search for, in order of preference.  The
   * current locale is always first on the list.  Aliases are not included
   * in the list -- use {@link #getAliases} to expand those.
   *
   * @return inheritance list
   */
  public List<GwtLocale> getInheritanceChain() {
    List<GwtLocale> inherits = new ArrayList<GwtLocale>();
    inherits.add(this);
    if (variant != null) {
      inherits.add(factory.fromComponents(language, script, region, null));
    }
    if (region != null) {
      addParentRegionLocales(factory, inherits, language, script, region,
          null);
      inherits.add(factory.fromComponents(language, script, null, null));
    }
    if (script != null) {
      inherits.add(factory.fromComponents(language, null, region, null));
      addParentRegionLocales(factory, inherits, language, null, region, null);
      if (region != null) {
        inherits.add(factory.fromComponents(language, null, null, null));
      }
    }
    if (language != null) {
      inherits.add(factory.fromComponents(null, null, null, null));
    }
    return inherits;
  }

  public String getLanguage() {
    return language;
  }

  public String getLanguageNotNull() {
    return language == null ? "" : language;
  }

  public String getRegion() {
    return region;
  }

  public String getRegionNotNull() {
    return region == null ? "" : region;
  }

  public String getScript() {
    return script;
  }

  public String getScriptNotNull() {
    return script == null ? "" : script;
  }

  public String getVariant() {
    return variant;
  }

  public String getVariantNotNull() {
    return variant == null ? "" : variant;
  }

  @Override
  public int hashCode() {
    int result = ((language == null) ? 0 : language.hashCode());
    result = 37 * result + ((region == null) ? 0 : region.hashCode());
    result = 43 * result + ((script == null) ? 0 : script.hashCode());
    result = 53 * result + ((variant == null) ? 0 : variant.hashCode());
    return result;
  }

  /**
   * Return true if this locale inherits from the specified locale.  Note that
   * locale.inheritsFrom(locale) is false -- if you want that to be true, you
   * should just use locale.getInheritanceChain().contains(x).
   *
   * @param parent locale to test against
   * @return true if parent is an ancestor of this locale
   */
  public boolean inheritsFrom(GwtLocale parent) {
    if (equals(parent)) {
      return false;
    }
    return getInheritanceChain().contains(parent);
  }

  public boolean isDefault() {
    return language == null;
  }

  @Override
  public String toString() {
    if (language == null) {
      return DEFAULT_LOCALE;
    }
    return getAsString();
  }

  /**
   * Checks if this locale uses the same script as another locale, taking into
   * account default scripts.
   *
   * @param other
   * @return true if the scripts are the same
   */
  public boolean usesSameScript(GwtLocale other) {
    // The number of aliases is very small, so n^2 isn't a problem here.
    List<GwtLocale> myAliases = getAliases();
    List<GwtLocale> otherAliases = other.getAliases();
    for (GwtLocale alias : myAliases) {
      for (GwtLocale otherAlias : otherAliases) {
        if (equalsNullCheck(alias.getScript(), otherAlias.getScript())) {
          return true;
        }
      }
    }
    return false;
  }
}
TOP

Related Classes of com.google.gwt.i18n.server.GwtLocaleImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.