NLS.java

  1. /*
  2.  * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.nls;

  11. import java.util.Locale;
  12. import java.util.Map;
  13. import java.util.concurrent.ConcurrentHashMap;

  14. import org.eclipse.jgit.errors.TranslationBundleLoadingException;
  15. import org.eclipse.jgit.errors.TranslationStringMissingException;

  16. /**
  17.  * The purpose of this class is to provide NLS (National Language Support)
  18.  * configurable per thread.
  19.  *
  20.  * <p>
  21.  * The {@link #setLocale(Locale)} method is used to configure locale for the
  22.  * calling thread. The locale setting is thread inheritable. This means that a
  23.  * child thread will have the same locale setting as its creator thread until it
  24.  * changes it explicitly.
  25.  *
  26.  * <p>
  27.  * Example of usage:
  28.  *
  29.  * <pre>
  30.  * NLS.setLocale(Locale.GERMAN);
  31.  * TransportText t = NLS.getBundleFor(TransportText.class);
  32.  * </pre>
  33.  */
  34. public class NLS {
  35.     /**
  36.      * The root locale constant. It is defined here because the Locale.ROOT is
  37.      * not defined in Java 5
  38.      */
  39.     public static final Locale ROOT_LOCALE = new Locale("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

  40.     private static final InheritableThreadLocal<NLS> local = new InheritableThreadLocal<>();

  41.     /**
  42.      * Sets the locale for the calling thread.
  43.      * <p>
  44.      * The {@link #getBundleFor(Class)} method will honor this setting if it
  45.      * is supported by the provided resource bundle property files. Otherwise,
  46.      * it will use a fall back locale as described in the
  47.      * {@link TranslationBundle}
  48.      *
  49.      * @param locale
  50.      *            the preferred locale
  51.      */
  52.     public static void setLocale(Locale locale) {
  53.         local.set(new NLS(locale));
  54.     }

  55.     /**
  56.      * Sets the JVM default locale as the locale for the calling thread.
  57.      * <p>
  58.      * Semantically this is equivalent to
  59.      * <code>NLS.setLocale(Locale.getDefault())</code>.
  60.      */
  61.     public static void useJVMDefaultLocale() {
  62.         useJVMDefaultInternal();
  63.     }

  64.     // TODO(ms): change signature of public useJVMDefaultLocale() in 5.0 to get
  65.     // rid of this internal method
  66.     private static NLS useJVMDefaultInternal() {
  67.         NLS b = new NLS(Locale.getDefault());
  68.         local.set(b);
  69.         return b;
  70.     }

  71.     /**
  72.      * Returns an instance of the translation bundle of the required type. All
  73.      * public String fields of the bundle instance will get their values
  74.      * injected as described in the
  75.      * {@link org.eclipse.jgit.nls.TranslationBundle}.
  76.      *
  77.      * @param type
  78.      *            required bundle type
  79.      * @return an instance of the required bundle type
  80.      * @exception TranslationBundleLoadingException
  81.      *                see
  82.      *                {@link org.eclipse.jgit.errors.TranslationBundleLoadingException}
  83.      * @exception TranslationStringMissingException
  84.      *                see
  85.      *                {@link org.eclipse.jgit.errors.TranslationStringMissingException}
  86.      */
  87.     public static <T extends TranslationBundle> T getBundleFor(Class<T> type) {
  88.         NLS b = local.get();
  89.         if (b == null) {
  90.             b = useJVMDefaultInternal();
  91.         }
  92.         return b.get(type);
  93.     }

  94.     /**
  95.      * Release resources held by NLS
  96.      * @since 5.8
  97.      */
  98.     public static void clear() {
  99.         local.remove();
  100.         GlobalBundleCache.clear();
  101.     }

  102.     private final Locale locale;

  103.     private final Map<Class, TranslationBundle> map = new ConcurrentHashMap<>();

  104.     private NLS(Locale locale) {
  105.         this.locale = locale;
  106.     }

  107.     @SuppressWarnings("unchecked")
  108.     private <T extends TranslationBundle> T get(Class<T> type) {
  109.         TranslationBundle bundle = map.get(type);
  110.         if (bundle == null) {
  111.             bundle = GlobalBundleCache.lookupBundle(locale, type);
  112.             // There is a small opportunity for a race, which we may
  113.             // lose. Accept defeat and return the winner's instance.
  114.             TranslationBundle old = map.putIfAbsent(type, bundle);
  115.             if (old != null)
  116.                 bundle = old;
  117.         }
  118.         return (T) bundle;
  119.     }
  120. }