blob: f80d8c43f9542afa001c02db2676273760e7c8b3 [file] [log] [blame]
Simon Hunt0c85f112017-06-12 21:02:17 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Simon Hunt0c85f112017-06-12 21:02:17 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.onosproject.ui.lion;
19
Simon Huntb8042032017-06-13 15:03:23 -070020import org.slf4j.Logger;
21import org.slf4j.LoggerFactory;
22
Simon Hunt7648cac2017-06-22 08:58:19 -070023import java.io.IOException;
24import java.io.InputStream;
25import java.io.InputStreamReader;
26import java.net.URL;
27import java.net.URLConnection;
Ray Milkey28b15ae2018-10-18 11:36:10 -070028import java.util.HashSet;
Simon Huntb8042032017-06-13 15:03:23 -070029import java.util.Locale;
Simon Hunt7648cac2017-06-22 08:58:19 -070030import java.util.PropertyResourceBundle;
Simon Hunt0c85f112017-06-12 21:02:17 -070031import java.util.ResourceBundle;
Simon Huntb8042032017-06-13 15:03:23 -070032import java.util.Set;
Simon Hunt0c85f112017-06-12 21:02:17 -070033
34/**
35 * Utility methods for dealing with Localization Bundles etc.
Simon Hunt7648cac2017-06-22 08:58:19 -070036 * <p>
37 * Note that each of the {@link #getBundledResource} methods use a custom
38 * {@link ResourceBundle.Control} instance which reads in the input stream
39 * using UTF-8.
Simon Hunt0c85f112017-06-12 21:02:17 -070040 */
41public final class LionUtils {
42
Simon Huntb8042032017-06-13 15:03:23 -070043 private static final Logger log = LoggerFactory.getLogger(LionUtils.class);
44
45 private static final String USER_LANGUAGE = "user.language";
46 private static final String USER_COUNTRY = "user.country";
47 private static final String ONOS_LOCALE = "ONOS_LOCALE";
48 private static final String EMPTY = "";
Simon Hunt0c85f112017-06-12 21:02:17 -070049 private static final String DOT = ".";
Simon Huntb8042032017-06-13 15:03:23 -070050 private static final String LOBAR = "_";
Simon Hunt0c85f112017-06-12 21:02:17 -070051
52 // no instantiation
53 private LionUtils() {
54 }
55
56 /**
Simon Huntb8042032017-06-13 15:03:23 -070057 * Parses the given string into language and country codes, and returns
58 * a {@link Locale} instance initialized with those parameters.
59 * For example:
60 * <pre>
61 * Locale locale = LionUtils.localeFromString("en_GB");
62 * locale.getLanguage(); // "en"
63 * locale.getCountry(); // "GB"
64 *
65 * locale = LionUtils.localeFromString("ru");
66 * locale.getLanguage(); // "ru"
67 * locale.getCountry(); // ""
68 * </pre>
69 *
70 * @param s the locale string
71 * @return a locale instance
72 */
73 public static Locale localeFromString(String s) {
74
75 if (!s.contains(LOBAR)) {
76 return new Locale(s);
77 }
78 String[] items = s.split(LOBAR);
79 return new Locale(items[0], items[1]);
80 }
81
82 /**
83 * Sets the default locale, based on the Java properties shown below.
84 * <pre>
85 * user.language
86 * user.country
87 * </pre>
88 * It is expected that the host system will have set these properties
89 * appropriately. Note, however, that the default values can be
90 * overridden by use of the environment variable {@code ONOS_LOCALE}.
91 * <p>
92 * For example, to set the Locale to French-Canadian one can invoke
93 * (from the shell)...
94 * <pre>
95 * $ ONOS_LOCALE=fr_CA {command-to-invoke-onos} ...
96 * </pre>
97 *
98 * @return the runtime locale
99 */
100 public static Locale setupRuntimeLocale() {
101 Locale systemDefault = Locale.getDefault();
102 log.info("System Default Locale: [{}]", systemDefault);
103 // TODO: Review- do we need to store the system default anywhere?
104
105 // Useful to log the "user.*" properties for debugging...
Ray Milkey28b15ae2018-10-18 11:36:10 -0700106 Set<String> pn = new HashSet<>(System.getProperties().stringPropertyNames());
Simon Huntb8042032017-06-13 15:03:23 -0700107 pn.removeIf(f -> !(f.startsWith("user.")));
108 for (String ukey : pn) {
109 log.debug(" {}: {}", ukey, System.getProperty(ukey));
110 }
111
112 String language = System.getProperty(USER_LANGUAGE);
113 String country = System.getProperty(USER_COUNTRY);
114 log.info("Language: [{}], Country: [{}]", language, country);
115 Locale runtime = new Locale(language != null ? language : EMPTY,
116 country != null ? country : EMPTY);
117
118 String override = System.getenv(ONOS_LOCALE);
119 if (override != null) {
120 log.warn("Override with ONOS_LOCALE: [{}]", override);
121 runtime = localeFromString(override);
122 }
123
124 log.info("Setting runtime locale to: [{}]", runtime);
125 Locale.setDefault(runtime);
126 return runtime;
127 }
128
129 /**
Simon Hunt0c85f112017-06-12 21:02:17 -0700130 * This method takes a fully qualified name and returns a
131 * {@link ResourceBundle} which is loaded from a properties file with
132 * that base name.
133 * <p>
134 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700135 * <pre>
136 * org/onosproject/util/example/SomeBundle.properties
137 * </pre>
138 * <p>
139 * Then, to correctly load the resource bundle associated with
140 * <code>SomeBundle</code>, call:
141 * <pre>
142 * String fqname = "org.onosproject.util.example.SomeBundle";
143 * ResourceBundle res = ResourceUtils.getBundledResource(fqname);
144 * </pre>
145 * <p>
146 * Note that no error is thrown if the properties file does not exist.
147 * This condition will not become apparent until you try and access
148 * a property from the bundle, at which time a
149 * {@link java.util.MissingResourceException} will be thrown.
150 *
Simon Hunt7648cac2017-06-22 08:58:19 -0700151 * @param basename the (fully qualified) basename of the bundle
152 * properties file
Simon Hunt0c85f112017-06-12 21:02:17 -0700153 * @return the associated resource bundle
154 */
155 public static ResourceBundle getBundledResource(String basename) {
Simon Hunt7648cac2017-06-22 08:58:19 -0700156 return ResourceBundle.getBundle(basename, new UTF8Control());
157 }
158
159 /**
160 * This method takes a fully qualified name and returns a
161 * {@link ResourceBundle} which is loaded from a properties file with
162 * that base name. The locale to use for bundle selection, and the
163 * class loader to use for the search path are also specified.
164 *
165 * @param basename the (fully qualified) basename of the bundle
166 * properties file
167 * @param locale the locale
168 * @param classLoader the class loader
169 * @return the appropriate resource bundle
170 */
171 public static ResourceBundle getBundledResource(String basename,
172 Locale locale,
173 ClassLoader classLoader) {
174 return ResourceBundle.getBundle(basename, locale, classLoader,
175 new UTF8Control());
Simon Hunt0c85f112017-06-12 21:02:17 -0700176 }
177
178 /**
179 * This method takes a class and returns a {@link ResourceBundle} which is
180 * loaded from a properties file with the same base name as the class.
181 * Note that both the class and the properties file(s) need to be in
182 * the same jar file.
183 * <p>
184 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700185 * <pre>
186 * org/onosproject/util/example/SomeObject.class
187 * org/onosproject/util/example/SomeObject.properties
188 * </pre>
189 * <p>
190 * Then, to correctly load the resource bundle associated with
191 * <code>SomeObject</code>, call:
192 * <pre>
193 * ResourceBundle res = ResourceUtils.getBundledResource(SomeObject.class);
194 * </pre>
195 * <p>
196 * Note that no error is thrown if the properties file does not exist.
197 * This condition will not become apparent until you try and access
198 * a property from the bundle, at which time a
199 * {@link java.util.MissingResourceException} will be thrown.
200 *
201 * @param c the class
202 * @return the associated resource bundle
203 */
204 public static ResourceBundle getBundledResource(Class<?> c) {
Simon Hunt7648cac2017-06-22 08:58:19 -0700205 return ResourceBundle.getBundle(c.getName(), new UTF8Control());
Simon Hunt0c85f112017-06-12 21:02:17 -0700206 }
207
208 /**
209 * This method returns a {@link ResourceBundle} which is loaded from
210 * a properties file with the specified base name from the same package
211 * as the specified class.
212 * Note that both the class and the properties file(s) need to be in
213 * the same jar file.
214 * <p>
215 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700216 * <pre>
217 * org/onosproject/util/example/SomeObject.class
218 * org/onosproject/util/example/DisplayStrings.properties
219 * </pre>
220 * <p>
221 * Then, to correctly load the resource bundle call:
222 * <pre>
223 * ResourceBundle res = ResourceUtils.getBundledResource(SomeObject.class,
224 * "DisplayStrings");
225 * </pre>
226 * <p>
227 * Note that no error is thrown if the properties file does not exist.
228 * This condition will not become apparent until you try and access
229 * a property from the bundle, at which time a
230 * {@link java.util.MissingResourceException} will be thrown.
231 *
232 * @param c the class requesting the bundle
233 * @param baseName the base name of the resource bundle
234 * @return the associated resource bundle
235 */
236 public static ResourceBundle getBundledResource(Class<?> c, String baseName) {
237 String className = c.getName();
238 StringBuilder sb = new StringBuilder();
239 int dot = className.lastIndexOf(DOT);
240 sb.append(className.substring(0, dot));
241 sb.append(DOT).append(baseName);
Simon Hunt7648cac2017-06-22 08:58:19 -0700242
243 return ResourceBundle.getBundle(sb.toString(), new UTF8Control());
244 }
245
246 /*
247 * Private implementation of a Control for reading .properties files
248 * using UTF-8 (rather than the default ISO-8859-1).
249 *
250 * Ref: https://stackoverflow.com/questions/4659929/
251 * how-to-use-utf-8-in-resource-properties-with-resourcebundle
252 */
253 private static class UTF8Control extends ResourceBundle.Control {
254 private static final String PROPERTIES = "properties";
255 private static final String UTF_8 = "UTF-8";
256
257 @Override
258 public ResourceBundle newBundle(String baseName, Locale locale,
259 String format, ClassLoader loader,
260 boolean reload)
261 throws IllegalAccessException, InstantiationException, IOException {
262
263 // Copy of (some older version of) the default implementation...
264 String bundleName = toBundleName(baseName, locale);
265 String resourceName = toResourceName(bundleName, PROPERTIES);
266 ResourceBundle bundle = null;
267 InputStream stream = null;
268 if (reload) {
269 URL url = loader.getResource(resourceName);
270 if (url != null) {
271 URLConnection connection = url.openConnection();
272 if (connection != null) {
273 connection.setUseCaches(false);
274 stream = connection.getInputStream();
275 }
276 }
277 } else {
278 stream = loader.getResourceAsStream(resourceName);
279 }
280 if (stream != null) {
281 try {
282 // Only this line is changed to make it
283 // read .properties files as UTF-8:
284 bundle = new PropertyResourceBundle(
285 new InputStreamReader(stream, UTF_8)
286 );
287 } finally {
288 stream.close();
289 }
290 }
291 return bundle;
292 }
Simon Hunt0c85f112017-06-12 21:02:17 -0700293 }
Simon Hunt0c85f112017-06-12 21:02:17 -0700294}