View Javadoc
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 }