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 11 package org.eclipse.jgit.nls; 12 13 import java.util.Locale; 14 import java.util.concurrent.ConcurrentHashMap; 15 16 import org.eclipse.jgit.errors.TranslationBundleLoadingException; 17 import org.eclipse.jgit.errors.TranslationStringMissingException; 18 19 /** 20 * The purpose of this class is to provide NLS (National Language Support) 21 * configurable per thread. 22 * 23 * <p> 24 * The {@link #setLocale(Locale)} method is used to configure locale for the 25 * calling thread. The locale setting is thread inheritable. This means that a 26 * child thread will have the same locale setting as its creator thread until it 27 * changes it explicitly. 28 * 29 * <p> 30 * Example of usage: 31 * 32 * <pre> 33 * NLS.setLocale(Locale.GERMAN); 34 * TransportText t = NLS.getBundleFor(TransportText.class); 35 * </pre> 36 */ 37 public class NLS { 38 /** 39 * The root locale constant. It is defined here because the Locale.ROOT is 40 * not defined in Java 5 41 */ 42 public static final Locale ROOT_LOCALE = new Locale("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 43 44 private static final InheritableThreadLocal<NLS> local = new InheritableThreadLocal<>(); 45 46 /** 47 * Sets the locale for the calling thread. 48 * <p> 49 * The {@link #getBundleFor(Class)} method will honor this setting if it 50 * is supported by the provided resource bundle property files. Otherwise, 51 * it will use a fall back locale as described in the 52 * {@link TranslationBundle} 53 * 54 * @param locale 55 * the preferred locale 56 */ 57 public static void setLocale(Locale locale) { 58 local.set(new NLS(locale)); 59 } 60 61 /** 62 * Sets the JVM default locale as the locale for the calling thread. 63 * <p> 64 * Semantically this is equivalent to 65 * <code>NLS.setLocale(Locale.getDefault())</code>. 66 */ 67 public static void useJVMDefaultLocale() { 68 useJVMDefaultInternal(); 69 } 70 71 // TODO(ms): change signature of public useJVMDefaultLocale() in 5.0 to get 72 // rid of this internal method 73 private static NLS useJVMDefaultInternal() { 74 NLS b = new NLS(Locale.getDefault()); 75 local.set(b); 76 return b; 77 } 78 79 /** 80 * Returns an instance of the translation bundle of the required type. All 81 * public String fields of the bundle instance will get their values 82 * injected as described in the 83 * {@link org.eclipse.jgit.nls.TranslationBundle}. 84 * 85 * @param type 86 * required bundle type 87 * @return an instance of the required bundle type 88 * @exception TranslationBundleLoadingException 89 * see 90 * {@link org.eclipse.jgit.errors.TranslationBundleLoadingException} 91 * @exception TranslationStringMissingException 92 * see 93 * {@link org.eclipse.jgit.errors.TranslationStringMissingException} 94 */ 95 public static <T extends TranslationBundle> T getBundleFor(Class<T> type) { 96 NLS b = local.get(); 97 if (b == null) { 98 b = useJVMDefaultInternal(); 99 } 100 return b.get(type); 101 } 102 103 private final Locale locale; 104 private final ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>(); 105 106 private NLS(Locale locale) { 107 this.locale = locale; 108 } 109 110 @SuppressWarnings("unchecked") 111 private <T extends TranslationBundle> T get(Class<T> type) { 112 TranslationBundle bundle = map.get(type); 113 if (bundle == null) { 114 bundle = GlobalBundleCache.lookupBundle(locale, type); 115 // There is a small opportunity for a race, which we may 116 // lose. Accept defeat and return the winner's instance. 117 TranslationBundle old = map.putIfAbsent(type, bundle); 118 if (old != null) 119 bundle = old; 120 } 121 return (T) bundle; 122 } 123 }