blob: 210a5af12db67aa6d2aff599a9bdb3250b4fd61c [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;
Simon Huntb8042032017-06-13 15:03:23 -070028import java.util.Locale;
Simon Hunt7648cac2017-06-22 08:58:19 -070029import java.util.PropertyResourceBundle;
Simon Hunt0c85f112017-06-12 21:02:17 -070030import java.util.ResourceBundle;
Simon Huntb8042032017-06-13 15:03:23 -070031import java.util.Set;
Simon Hunt0c85f112017-06-12 21:02:17 -070032
33/**
34 * Utility methods for dealing with Localization Bundles etc.
Simon Hunt7648cac2017-06-22 08:58:19 -070035 * <p>
36 * Note that each of the {@link #getBundledResource} methods use a custom
37 * {@link ResourceBundle.Control} instance which reads in the input stream
38 * using UTF-8.
Simon Hunt0c85f112017-06-12 21:02:17 -070039 */
40public final class LionUtils {
41
Simon Huntb8042032017-06-13 15:03:23 -070042 private static final Logger log = LoggerFactory.getLogger(LionUtils.class);
43
44 private static final String USER_LANGUAGE = "user.language";
45 private static final String USER_COUNTRY = "user.country";
46 private static final String ONOS_LOCALE = "ONOS_LOCALE";
47 private static final String EMPTY = "";
Simon Hunt0c85f112017-06-12 21:02:17 -070048 private static final String DOT = ".";
Simon Huntb8042032017-06-13 15:03:23 -070049 private static final String LOBAR = "_";
Simon Hunt0c85f112017-06-12 21:02:17 -070050
51 // no instantiation
52 private LionUtils() {
53 }
54
55 /**
Simon Huntb8042032017-06-13 15:03:23 -070056 * Parses the given string into language and country codes, and returns
57 * a {@link Locale} instance initialized with those parameters.
58 * For example:
59 * <pre>
60 * Locale locale = LionUtils.localeFromString("en_GB");
61 * locale.getLanguage(); // "en"
62 * locale.getCountry(); // "GB"
63 *
64 * locale = LionUtils.localeFromString("ru");
65 * locale.getLanguage(); // "ru"
66 * locale.getCountry(); // ""
67 * </pre>
68 *
69 * @param s the locale string
70 * @return a locale instance
71 */
72 public static Locale localeFromString(String s) {
73
74 if (!s.contains(LOBAR)) {
75 return new Locale(s);
76 }
77 String[] items = s.split(LOBAR);
78 return new Locale(items[0], items[1]);
79 }
80
81 /**
82 * Sets the default locale, based on the Java properties shown below.
83 * <pre>
84 * user.language
85 * user.country
86 * </pre>
87 * It is expected that the host system will have set these properties
88 * appropriately. Note, however, that the default values can be
89 * overridden by use of the environment variable {@code ONOS_LOCALE}.
90 * <p>
91 * For example, to set the Locale to French-Canadian one can invoke
92 * (from the shell)...
93 * <pre>
94 * $ ONOS_LOCALE=fr_CA {command-to-invoke-onos} ...
95 * </pre>
96 *
97 * @return the runtime locale
98 */
99 public static Locale setupRuntimeLocale() {
100 Locale systemDefault = Locale.getDefault();
101 log.info("System Default Locale: [{}]", systemDefault);
102 // TODO: Review- do we need to store the system default anywhere?
103
104 // Useful to log the "user.*" properties for debugging...
105 Set<String> pn = System.getProperties().stringPropertyNames();
106 pn.removeIf(f -> !(f.startsWith("user.")));
107 for (String ukey : pn) {
108 log.debug(" {}: {}", ukey, System.getProperty(ukey));
109 }
110
111 String language = System.getProperty(USER_LANGUAGE);
112 String country = System.getProperty(USER_COUNTRY);
113 log.info("Language: [{}], Country: [{}]", language, country);
114 Locale runtime = new Locale(language != null ? language : EMPTY,
115 country != null ? country : EMPTY);
116
117 String override = System.getenv(ONOS_LOCALE);
118 if (override != null) {
119 log.warn("Override with ONOS_LOCALE: [{}]", override);
120 runtime = localeFromString(override);
121 }
122
123 log.info("Setting runtime locale to: [{}]", runtime);
124 Locale.setDefault(runtime);
125 return runtime;
126 }
127
128 /**
Simon Hunt0c85f112017-06-12 21:02:17 -0700129 * This method takes a fully qualified name and returns a
130 * {@link ResourceBundle} which is loaded from a properties file with
131 * that base name.
132 * <p>
133 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700134 * <pre>
135 * org/onosproject/util/example/SomeBundle.properties
136 * </pre>
137 * <p>
138 * Then, to correctly load the resource bundle associated with
139 * <code>SomeBundle</code>, call:
140 * <pre>
141 * String fqname = "org.onosproject.util.example.SomeBundle";
142 * ResourceBundle res = ResourceUtils.getBundledResource(fqname);
143 * </pre>
144 * <p>
145 * Note that no error is thrown if the properties file does not exist.
146 * This condition will not become apparent until you try and access
147 * a property from the bundle, at which time a
148 * {@link java.util.MissingResourceException} will be thrown.
149 *
Simon Hunt7648cac2017-06-22 08:58:19 -0700150 * @param basename the (fully qualified) basename of the bundle
151 * properties file
Simon Hunt0c85f112017-06-12 21:02:17 -0700152 * @return the associated resource bundle
153 */
154 public static ResourceBundle getBundledResource(String basename) {
Simon Hunt7648cac2017-06-22 08:58:19 -0700155 return ResourceBundle.getBundle(basename, new UTF8Control());
156 }
157
158 /**
159 * This method takes a fully qualified name and returns a
160 * {@link ResourceBundle} which is loaded from a properties file with
161 * that base name. The locale to use for bundle selection, and the
162 * class loader to use for the search path are also specified.
163 *
164 * @param basename the (fully qualified) basename of the bundle
165 * properties file
166 * @param locale the locale
167 * @param classLoader the class loader
168 * @return the appropriate resource bundle
169 */
170 public static ResourceBundle getBundledResource(String basename,
171 Locale locale,
172 ClassLoader classLoader) {
173 return ResourceBundle.getBundle(basename, locale, classLoader,
174 new UTF8Control());
Simon Hunt0c85f112017-06-12 21:02:17 -0700175 }
176
177 /**
178 * This method takes a class and returns a {@link ResourceBundle} which is
179 * loaded from a properties file with the same base name as the class.
180 * Note that both the class and the properties file(s) need to be in
181 * the same jar file.
182 * <p>
183 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700184 * <pre>
185 * org/onosproject/util/example/SomeObject.class
186 * org/onosproject/util/example/SomeObject.properties
187 * </pre>
188 * <p>
189 * Then, to correctly load the resource bundle associated with
190 * <code>SomeObject</code>, call:
191 * <pre>
192 * ResourceBundle res = ResourceUtils.getBundledResource(SomeObject.class);
193 * </pre>
194 * <p>
195 * Note that no error is thrown if the properties file does not exist.
196 * This condition will not become apparent until you try and access
197 * a property from the bundle, at which time a
198 * {@link java.util.MissingResourceException} will be thrown.
199 *
200 * @param c the class
201 * @return the associated resource bundle
202 */
203 public static ResourceBundle getBundledResource(Class<?> c) {
Simon Hunt7648cac2017-06-22 08:58:19 -0700204 return ResourceBundle.getBundle(c.getName(), new UTF8Control());
Simon Hunt0c85f112017-06-12 21:02:17 -0700205 }
206
207 /**
208 * This method returns a {@link ResourceBundle} which is loaded from
209 * a properties file with the specified base name from the same package
210 * as the specified class.
211 * Note that both the class and the properties file(s) need to be in
212 * the same jar file.
213 * <p>
214 * For example, supposing the jar file contains:
Simon Hunt0c85f112017-06-12 21:02:17 -0700215 * <pre>
216 * org/onosproject/util/example/SomeObject.class
217 * org/onosproject/util/example/DisplayStrings.properties
218 * </pre>
219 * <p>
220 * Then, to correctly load the resource bundle call:
221 * <pre>
222 * ResourceBundle res = ResourceUtils.getBundledResource(SomeObject.class,
223 * "DisplayStrings");
224 * </pre>
225 * <p>
226 * Note that no error is thrown if the properties file does not exist.
227 * This condition will not become apparent until you try and access
228 * a property from the bundle, at which time a
229 * {@link java.util.MissingResourceException} will be thrown.
230 *
231 * @param c the class requesting the bundle
232 * @param baseName the base name of the resource bundle
233 * @return the associated resource bundle
234 */
235 public static ResourceBundle getBundledResource(Class<?> c, String baseName) {
236 String className = c.getName();
237 StringBuilder sb = new StringBuilder();
238 int dot = className.lastIndexOf(DOT);
239 sb.append(className.substring(0, dot));
240 sb.append(DOT).append(baseName);
Simon Hunt7648cac2017-06-22 08:58:19 -0700241
242 return ResourceBundle.getBundle(sb.toString(), new UTF8Control());
243 }
244
245 /*
246 * Private implementation of a Control for reading .properties files
247 * using UTF-8 (rather than the default ISO-8859-1).
248 *
249 * Ref: https://stackoverflow.com/questions/4659929/
250 * how-to-use-utf-8-in-resource-properties-with-resourcebundle
251 */
252 private static class UTF8Control extends ResourceBundle.Control {
253 private static final String PROPERTIES = "properties";
254 private static final String UTF_8 = "UTF-8";
255
256 @Override
257 public ResourceBundle newBundle(String baseName, Locale locale,
258 String format, ClassLoader loader,
259 boolean reload)
260 throws IllegalAccessException, InstantiationException, IOException {
261
262 // Copy of (some older version of) the default implementation...
263 String bundleName = toBundleName(baseName, locale);
264 String resourceName = toResourceName(bundleName, PROPERTIES);
265 ResourceBundle bundle = null;
266 InputStream stream = null;
267 if (reload) {
268 URL url = loader.getResource(resourceName);
269 if (url != null) {
270 URLConnection connection = url.openConnection();
271 if (connection != null) {
272 connection.setUseCaches(false);
273 stream = connection.getInputStream();
274 }
275 }
276 } else {
277 stream = loader.getResourceAsStream(resourceName);
278 }
279 if (stream != null) {
280 try {
281 // Only this line is changed to make it
282 // read .properties files as UTF-8:
283 bundle = new PropertyResourceBundle(
284 new InputStreamReader(stream, UTF_8)
285 );
286 } finally {
287 stream.close();
288 }
289 }
290 return bundle;
291 }
Simon Hunt0c85f112017-06-12 21:02:17 -0700292 }
Simon Hunt0c85f112017-06-12 21:02:17 -0700293}