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 }