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