Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
LocalizedMessages |
|
| 1.7272727272727273;1,727 |
1 | /* | |
2 | * FCKeditor - The text editor for Internet - http://www.fckeditor.net | |
3 | * Copyright (C) 2004-2010 Frederico Caldeira Knabben | |
4 | * | |
5 | * == BEGIN LICENSE == | |
6 | * | |
7 | * Licensed under the terms of any of the following licenses at your | |
8 | * choice: | |
9 | * | |
10 | * - GNU General Public License Version 2 or later (the "GPL") | |
11 | * http://www.gnu.org/licenses/gpl.html | |
12 | * | |
13 | * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") | |
14 | * http://www.gnu.org/licenses/lgpl.html | |
15 | * | |
16 | * - Mozilla Public License Version 1.1 or later (the "MPL") | |
17 | * http://www.mozilla.org/MPL/MPL-1.1.html | |
18 | * | |
19 | * == END LICENSE == | |
20 | */ | |
21 | package net.fckeditor.localization; | |
22 | ||
23 | import java.io.BufferedInputStream; | |
24 | import java.io.InputStream; | |
25 | import java.text.MessageFormat; | |
26 | import java.util.Collections; | |
27 | import java.util.Enumeration; | |
28 | import java.util.HashMap; | |
29 | import java.util.Locale; | |
30 | import java.util.Map; | |
31 | import java.util.Properties; | |
32 | import java.util.ResourceBundle; | |
33 | ||
34 | import javax.servlet.http.HttpServletRequest; | |
35 | ||
36 | import net.fckeditor.handlers.PropertiesLoader; | |
37 | import net.fckeditor.tool.Utils; | |
38 | ||
39 | import org.slf4j.Logger; | |
40 | import org.slf4j.LoggerFactory; | |
41 | ||
42 | /** | |
43 | * Provides access to localized messages (properties). | |
44 | * <p> | |
45 | * Localized messages are loaded for a particular locale from a HTTP request. | |
46 | * The locale is resolved by the current {@link LocaleResolver} | |
47 | * instance/singleton. If a locale or a bundle for a locale cannot be found, | |
48 | * default messages are used. | |
49 | * </p> | |
50 | * <p> | |
51 | * Note: Loaded messages are cached per locale, any subsequent call of the same | |
52 | * locale will be served by the cache instead of another resource bundle | |
53 | * retrieval. | |
54 | * </p> | |
55 | * | |
56 | * @version $Id: LocalizedMessages.java 4785 2009-12-21 20:10:28Z mosipov $ | |
57 | */ | |
58 | public class LocalizedMessages { | |
59 | ||
60 | 0 | private static final Map<Locale, LocalizedMessages> prototypes = Collections |
61 | .synchronizedMap(new HashMap<Locale, LocalizedMessages>()); | |
62 | private static final String DEFAULT_FILENAME = "default_messages.properties"; //$NON-NLS-1$ | |
63 | private static final String LOCAL_PROPERTIES = "fckeditor_messages"; //$NON-NLS-1$ | |
64 | 0 | private static final Properties defaultProperties = new Properties(); |
65 | ||
66 | private Properties properties; | |
67 | private static LocaleResolver localeResolver; | |
68 | 0 | private static final Locale NEUTRAL = new Locale(Utils.EMPTY_STRING); //$NON-NLS-1$ |
69 | 0 | private static final Logger logger = LoggerFactory |
70 | 0 | .getLogger(LocalizedMessages.class); |
71 | ||
72 | static { | |
73 | ||
74 | 0 | InputStream in = LocalizedMessages.class |
75 | .getResourceAsStream(DEFAULT_FILENAME); | |
76 | ||
77 | 0 | if (in == null) { |
78 | 0 | logger.error("{} not found", DEFAULT_FILENAME); //$NON-NLS-1$ |
79 | 0 | throw new RuntimeException(DEFAULT_FILENAME + " not found"); //$NON-NLS-1$ |
80 | } else { | |
81 | 0 | if (!(in instanceof BufferedInputStream)) |
82 | 0 | in = new BufferedInputStream(in); |
83 | ||
84 | try { | |
85 | 0 | defaultProperties.load(in); |
86 | 0 | in.close(); |
87 | 0 | logger.debug("{} loaded", DEFAULT_FILENAME); //$NON-NLS-1$ |
88 | 0 | } catch (Exception e) { |
89 | 0 | logger.error("Error while loading {}", DEFAULT_FILENAME); //$NON-NLS-1$ |
90 | 0 | throw new RuntimeException( |
91 | "Error while loading " + DEFAULT_FILENAME, e); //$NON-NLS-1$ | |
92 | 0 | } |
93 | } | |
94 | 0 | } |
95 | ||
96 | /** | |
97 | * Returns an instance of <code>LocalizedMessages</code> for a given | |
98 | * request. This method automatically determines the locale of this request | |
99 | * and loads the appropriate bundle. If locale is null or not available, | |
100 | * the default locale will be used. | |
101 | * | |
102 | * @param request | |
103 | * the current request instance | |
104 | * @return instance with localized messages | |
105 | */ | |
106 | public static LocalizedMessages getInstance(HttpServletRequest request) { | |
107 | ||
108 | 0 | if (request == null) |
109 | 0 | throw new NullPointerException("the request cannot be null"); |
110 | ||
111 | 0 | Locale locale = getLocaleResolverInstance().resolveLocale(request); |
112 | ||
113 | 0 | if (locale == null) |
114 | 0 | locale = NEUTRAL; |
115 | ||
116 | 0 | synchronized (LocalizedMessages.class) { |
117 | 0 | if (!prototypes.containsKey(locale)) |
118 | 0 | prototypes.put(locale, new LocalizedMessages(locale)); |
119 | 0 | } |
120 | ||
121 | // for now we don't need any cloning since the values are accessed | |
122 | // read-only | |
123 | 0 | return prototypes.get(locale); |
124 | ||
125 | } | |
126 | ||
127 | /** | |
128 | * Returns the locale resolver instance. The implementation class name is | |
129 | * provided by {@link PropertiesLoader#getLocaleResolverImpl()}. | |
130 | * | |
131 | * @return the locale resolver instance | |
132 | */ | |
133 | private synchronized static LocaleResolver getLocaleResolverInstance() { | |
134 | ||
135 | 0 | if (localeResolver == null) { |
136 | 0 | String className = PropertiesLoader.getLocaleResolverImpl(); |
137 | ||
138 | 0 | if (Utils.isEmpty(className)) |
139 | 0 | logger.error("Empty LocaleResolver implementation class name provided"); //$NON-NLS-1$ |
140 | else | |
141 | try { | |
142 | 0 | Class<?> clazz = Class.forName(className); |
143 | 0 | localeResolver = (LocaleResolver) clazz.newInstance(); |
144 | 0 | logger.info("LocaleResolver initialized to {}", className); //$NON-NLS-1$ |
145 | 0 | } catch (Throwable e) { |
146 | 0 | logger.error("LocaleResolver implementation {} could not be instantiated", className); //$NON-NLS-1$ |
147 | 0 | throw new RuntimeException("LocaleResolver implementation " + className + " could not be instantiated", e); //$NON-NLS-1$ |
148 | 0 | } |
149 | } | |
150 | ||
151 | 0 | return localeResolver; |
152 | } | |
153 | ||
154 | /** | |
155 | * Loads the localized messages for the given locale. This constructor loads | |
156 | * the resource bundle for this locale and only for this, in other words it | |
157 | * short-circuits the default resource bundle load mechanism in order to | |
158 | * prevent the loading of the system default locale which may result in a | |
159 | * completely different resource bundle. | |
160 | * | |
161 | * @param locale | |
162 | * the locale of the new localized messages | |
163 | */ | |
164 | 0 | private LocalizedMessages(Locale locale) { |
165 | ||
166 | 0 | properties = new Properties(defaultProperties); |
167 | ||
168 | 0 | ResourceBundle localized = null; |
169 | try { | |
170 | 0 | localized = ResourceBundle.getBundle(LOCAL_PROPERTIES, locale, |
171 | Thread.currentThread().getContextClassLoader()); | |
172 | 0 | } catch (Exception e) { |
173 | ; // do nothing | |
174 | 0 | } |
175 | ||
176 | 0 | if (localized != null |
177 | && localized.getLocale().getLanguage().equals( | |
178 | locale.getLanguage())) { | |
179 | 0 | Enumeration<String> keys = localized.getKeys(); |
180 | ||
181 | 0 | while (keys.hasMoreElements()) { |
182 | 0 | String key = keys.nextElement(); |
183 | 0 | properties.setProperty(key, localized.getString(key)); |
184 | 0 | } |
185 | ||
186 | 0 | logger.debug("Resource bundle for locale '{}' loaded", locale); //$NON-NLS-1$ |
187 | 0 | } else { |
188 | 0 | logger.debug("No resource bundle for locale '{}' found, loading default bundle", locale); //$NON-NLS-1$ |
189 | ||
190 | 0 | ResourceBundle base = null; |
191 | try { | |
192 | 0 | base = ResourceBundle.getBundle(LOCAL_PROPERTIES, NEUTRAL, |
193 | Thread.currentThread().getContextClassLoader()); | |
194 | 0 | } catch (Exception e) { |
195 | ; // do nothing | |
196 | 0 | } |
197 | ||
198 | 0 | if (base != null && base.getLocale().equals(NEUTRAL)) { |
199 | 0 | Enumeration<String> keys = base.getKeys(); |
200 | ||
201 | 0 | while (keys.hasMoreElements()) { |
202 | 0 | String key = keys.nextElement(); |
203 | 0 | properties.setProperty(key, base.getString(key)); |
204 | 0 | } |
205 | } | |
206 | ||
207 | } | |
208 | ||
209 | 0 | } |
210 | ||
211 | /** | |
212 | * Searches for the message with the specified key in this message list. | |
213 | * | |
214 | * @see Properties#getProperty(String) | |
215 | */ | |
216 | private String getMessage(String key) { | |
217 | 0 | return properties.getProperty(key); |
218 | } | |
219 | ||
220 | /** Returns localized <code>editor.compatibleBrowser.yes</code> property. */ | |
221 | public String getCompatibleBrowserYes() { | |
222 | 0 | return getMessage("editor.compatibleBrowser.yes"); //$NON-NLS-1$ |
223 | } | |
224 | ||
225 | /** Returns localized <code>editor.compatibleBrowser.no</code> property. */ | |
226 | public String getCompatibleBrowserNo() { | |
227 | 0 | return getMessage("editor.compatibleBrowser.no"); //$NON-NLS-1$ |
228 | } | |
229 | ||
230 | /** Returns localized <code>connector.fileUpload.enabled</code> property. */ | |
231 | public String getFileUploadEnabled() { | |
232 | 0 | return getMessage("connector.fileUpload.enabled"); //$NON-NLS-1$ |
233 | } | |
234 | ||
235 | /** Returns localized <code>connector.fileUpload.disabled</code> property. */ | |
236 | public String getFileUploadDisabled() { | |
237 | 0 | return getMessage("connector.fileUpload.disabled"); //$NON-NLS-1$ |
238 | } | |
239 | ||
240 | /** | |
241 | * Returns localized <code>connector.file_renamed_warning</code> property. | |
242 | * | |
243 | * @param newFileName | |
244 | * the new filename of the warning | |
245 | * @return localized message with new filename | |
246 | */ | |
247 | public String getFileRenamedWarning(String newFileName) { | |
248 | 0 | return MessageFormat.format(getMessage("connector.fileUpload.file_renamed_warning"), newFileName); //$NON-NLS-1$ |
249 | } | |
250 | ||
251 | /** Returns localized <code>connector.fileUpload.invalid_file_type_specified</code> property. */ | |
252 | public String getInvalidFileTypeSpecified() { | |
253 | 0 | return getMessage("connector.fileUpload.invalid_file_type_specified"); //$NON-NLS-1$ |
254 | } | |
255 | ||
256 | /** Returns localized <code>connector.fileUpload.write_error</code> property. */ | |
257 | public String getFileUploadWriteError() { | |
258 | 0 | return getMessage("connector.fileUpload.write_error"); //$NON-NLS-1$ |
259 | } | |
260 | ||
261 | /** Returns localized <code>connector.getResources.enabled</code> property. */ | |
262 | public String getGetResourcesEnabled() { | |
263 | 0 | return getMessage("connector.getResources.enabled"); //$NON-NLS-1$ |
264 | } | |
265 | ||
266 | /** Returns localized <code>connector.getResources.disabled</code> property. */ | |
267 | public String getGetResourcesDisabled() { | |
268 | 0 | return getMessage("connector.getResources.disabled"); //$NON-NLS-1$ |
269 | } | |
270 | ||
271 | /** Returns localized <code>connector.getResources.read_error</code> property. */ | |
272 | public String getGetResourcesReadError() { | |
273 | 0 | return getMessage("connector.getResources.read_error"); //$NON-NLS-1$ |
274 | } | |
275 | ||
276 | /** Returns localized <code>connector.createFolder.enabled</code> property. */ | |
277 | public String getCreateFolderEnabled() { | |
278 | 0 | return getMessage("connector.createFolder.enabled"); //$NON-NLS-1$ |
279 | } | |
280 | ||
281 | /** Returns localized <code>connector.createFolder.disabled</code> property. */ | |
282 | public String getCreateFolderDisabled() { | |
283 | 0 | return getMessage("connector.createFolder.disabled"); //$NON-NLS-1$ |
284 | } | |
285 | ||
286 | /** Returns localized <code>connector.invalid_command_specified</code> property. */ | |
287 | public String getInvalidCommandSpecified() { | |
288 | 0 | return getMessage("connector.invalid_command_specified"); //$NON-NLS-1$ |
289 | } | |
290 | ||
291 | /** Returns localized <code>connector.createFolder.folder_already_exists_error</code> property. */ | |
292 | public String getFolderAlreadyExistsError() { | |
293 | 0 | return getMessage("connector.createFolder.folder_already_exists_error"); //$NON-NLS-1$ |
294 | } | |
295 | ||
296 | /** Returns localized <code>connector.createFolder.invalid_new_folder_name_specified</code> property. */ | |
297 | public String getInvalidNewFolderNameSpecified() { | |
298 | 0 | return getMessage("connector.createFolder.invalid_new_folder_name_specified"); //$NON-NLS-1$ |
299 | } | |
300 | ||
301 | /** Returns localized <code>connector.createFolder.write_error</code> property. */ | |
302 | public String getCreateFolderWriteError() { | |
303 | 0 | return getMessage("connector.createFolder.write_error"); //$NON-NLS-1$ |
304 | } | |
305 | ||
306 | /** Returns localized <code>connector.invalid_resource_type_specified</code> property. */ | |
307 | public String getInvalidResouceTypeSpecified() { | |
308 | 0 | return getMessage("connector.invalid_resource_type_specified"); //$NON-NLS-1$ |
309 | } | |
310 | ||
311 | /** Returns localized <code>connector.invalid_current_folder_specified</code> property. */ | |
312 | public String getInvalidCurrentFolderSpecified() { | |
313 | 0 | return getMessage("connector.invalid_current_folder_specified"); //$NON-NLS-1$ |
314 | } | |
315 | ||
316 | } |