View Javadoc

1   /*
2    * Copyright (c) 2010, Marco Brade
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * * Redistributions of source code must retain the above copyright notice, this
9    *   list of conditions and the following disclaimer.
10   *
11   * * Redistributions in binary form must reproduce the above copyright notice,
12   *   this list of conditions and the following disclaimer in the documentation
13   *   and/or other materials provided with the distribution.
14   *
15   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25   */
26  package net.sf.prefixedproperties;
27  
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.URL;
31  import java.net.URLConnection;
32  import java.security.AccessController;
33  import java.security.PrivilegedActionException;
34  import java.security.PrivilegedExceptionAction;
35  import java.util.ArrayList;
36  import java.util.Collections;
37  import java.util.Enumeration;
38  import java.util.Iterator;
39  import java.util.List;
40  import java.util.Locale;
41  import java.util.ResourceBundle;
42  
43  import net.sf.prefixedproperties.config.PrefixConfig;
44  
45  /**
46   * The PrefixedResourceBundle extends the {@link ResourceBundle} class to
47   */
48  public class PrefixedResourceBundle extends ResourceBundle {
49  
50  	/**
51  	 * The Class PrefixedControl.
52  	 */
53  	public static class PrefixedControl extends ResourceBundle.Control {
54  
55  		private final String defaultPrefix;
56  		private final PrefixConfig prefixConfig;
57  
58  		/** The Constant FORMAT_DEFAULT. */
59  		public static final List<String> FORMAT_DEFAULT;
60  
61  		/** The Constant FORMAT_XML. */
62  		public final static String FORMAT_XML = "pref.xml";
63  
64  		/** The Constant FORMAT_JSON. */
65  		public final static String FORMAT_JSON = "pref.json";
66  
67  		/** The Constant FORMAT_PROPERTIES. */
68  		public final static String FORMAT_PROPERTIES = "pref.properties";
69  
70  		static {
71  			final List<String> formatList = new ArrayList<String>(3);
72  			formatList.add(FORMAT_JSON);
73  			formatList.add(FORMAT_PROPERTIES);
74  			formatList.add(FORMAT_XML);
75  			FORMAT_DEFAULT = Collections.unmodifiableList(formatList);
76  		}
77  
78  		protected PrefixedControl() {
79  			this.defaultPrefix = null;
80  			this.prefixConfig = null;
81  		}
82  
83  		/**
84  		 * Instantiates a new prefixed control.
85  		 * 
86  		 * @param prefixConfig
87  		 *            the prefix config
88  		 */
89  		protected PrefixedControl(final PrefixConfig prefixConfig) {
90  			this.prefixConfig = prefixConfig;
91  			this.defaultPrefix = null;
92  		}
93  
94  		/**
95  		 * Instantiates a new prefixed control.
96  		 * 
97  		 * @param defaultPrefix
98  		 *            the default prefix
99  		 */
100 		protected PrefixedControl(final String defaultPrefix) {
101 			this.defaultPrefix = defaultPrefix;
102 			this.prefixConfig = null;
103 		}
104 
105 		private InputStream createInputStream(final ClassLoader loader, final boolean reload, final String resourceName)
106 				throws IOException {
107 			InputStream stream;
108 			try {
109 				stream = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
110 					@Override
111 					public InputStream run() throws IOException {
112 						InputStream is = null;
113 						if (reload) {
114 							final URL url = loader.getResource(resourceName);
115 							if (url != null) {
116 								final URLConnection connection = url.openConnection();
117 								if (connection != null) {
118 									connection.setUseCaches(false);
119 									is = connection.getInputStream();
120 								}
121 							}
122 						} else {
123 							is = loader.getResourceAsStream(resourceName);
124 						}
125 						return is;
126 					}
127 				});
128 			} catch (final PrivilegedActionException e) {
129 				throw (IOException) e.getException();
130 			}
131 			return stream;
132 		}
133 
134 		/*
135 		 * (non-Javadoc)
136 		 * 
137 		 * @see java.util.ResourceBundle.Control#getFormats(java.lang.String)
138 		 */
139 		@Override
140 		public List<String> getFormats(final String baseName) {
141 			if (baseName == null) {
142 				throw new NullPointerException();
143 			}
144 			return FORMAT_DEFAULT;
145 		}
146 
147 		/*
148 		 * (non-Javadoc)
149 		 * 
150 		 * @see java.util.ResourceBundle.Control#newBundle(java.lang.String,
151 		 * java.util.Locale, java.lang.String, java.lang.ClassLoader, boolean)
152 		 */
153 		@Override
154 		public ResourceBundle newBundle(final String baseName, final Locale locale, final String format,
155 				final ClassLoader loader, final boolean reload)
156 				throws IllegalAccessException, InstantiationException, IOException {
157 			final String bundleName = toBundleName(baseName, locale);
158 			InputStream stream = null;
159 			try {
160 				final PrefixedProperties properties = (prefixConfig != null) ? new PrefixedProperties(prefixConfig)
161 						: new PrefixedProperties();
162 				if (defaultPrefix != null) {
163 					properties.setDefaultPrefix(defaultPrefix);
164 				}
165 				if (FORMAT_JSON.equals(format)) {
166 					final String resourceName = toResourceName(bundleName, "json");
167 					stream = createInputStream(loader, reload, resourceName);
168 					if (stream != null) {
169 						properties.loadFromJSON(stream);
170 						return new PrefixedResourceBundle(properties);
171 					}
172 				} else if (FORMAT_PROPERTIES.equals(format)) {
173 					final String resourceName = toResourceName(bundleName, "properties");
174 					stream = createInputStream(loader, reload, resourceName);
175 					if (stream != null) {
176 						properties.load(stream);
177 						return new PrefixedResourceBundle(properties);
178 					}
179 				} else if (FORMAT_XML.equals(format)) {
180 					final String resourceName = toResourceName(bundleName, "xml");
181 					stream = createInputStream(loader, reload, resourceName);
182 					if (stream != null) {
183 						properties.loadFromXML(stream);
184 						return new PrefixedResourceBundle(properties);
185 					}
186 				} else {
187 					return super.newBundle(baseName, locale, format, loader, reload);
188 				}
189 			} finally {
190 				if (stream != null) {
191 					stream.close();
192 				}
193 			}
194 			return null;
195 		}
196 	}
197 
198 	@SuppressWarnings("rawtypes")
199 	private static class ResouceBundleEnumeration implements Enumeration {
200 
201 		private final Iterator it;
202 
203 		ResouceBundleEnumeration(final Iterator it) {
204 			this.it = it;
205 		}
206 
207 		@Override
208 		public boolean hasMoreElements() {
209 			return it.hasNext();
210 		}
211 
212 		@Override
213 		public Object nextElement() {
214 			return it.next();
215 		}
216 
217 	}
218 
219 	public static PrefixedResourceBundle getPrefixedResourceBundle(final String baseFileName, final Locale locale)
220 			throws IOException {
221 		ResourceBundle bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl());
222 		while (bundle != null && !(bundle instanceof PrefixedResourceBundle)) { // a
223 																				// prior
224 																				// call
225 																				// to
226 																				// getBundle
227 																				// might
228 																				// be
229 																				// called
230 																				// without
231 																				// the
232 																				// proper
233 																				// Control
234 																				// class.
235 																				// We
236 																				// need
237 																				// to
238 																				// clear
239 																				// the
240 																				// cache.
241 			ResourceBundle.clearCache();
242 			bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl());
243 		}
244 		return (PrefixedResourceBundle) bundle;
245 	}
246 
247 	/**
248 	 * Gets the prefixed resource bundle.
249 	 * 
250 	 * @param baseFileName
251 	 *            the base file name
252 	 * @param locale
253 	 *            the locale
254 	 * @param prefixConfig
255 	 *            the prefix config
256 	 * @return the prefixed resource bundle
257 	 * @throws IOException
258 	 *             Signals that an I/O exception has occurred.
259 	 */
260 	public static PrefixedResourceBundle getPrefixedResourceBundle(final String baseFileName, final Locale locale,
261 			final PrefixConfig prefixConfig) throws IOException {
262 		ResourceBundle bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl(prefixConfig));
263 		while (bundle != null && !(bundle instanceof PrefixedResourceBundle)) {// a
264 																				// prior
265 																				// call
266 																				// to
267 																				// getBundle
268 																				// might
269 																				// be
270 																				// called
271 																				// without
272 																				// the
273 																				// proper
274 																				// Control
275 																				// class.
276 																				// We
277 																				// need
278 																				// to
279 																				// clear
280 																				// the
281 																				// cache.
282 			ResourceBundle.clearCache();
283 			bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl(prefixConfig));
284 		}
285 		return (PrefixedResourceBundle) bundle;
286 	}
287 
288 	/**
289 	 * Gets the prefixed resource bundle.
290 	 * 
291 	 * @param baseFileName
292 	 *            the base file name
293 	 * @param locale
294 	 *            the locale
295 	 * @param defaultPrefix
296 	 *            the default prefix
297 	 * @return the prefixed resource bundle
298 	 * @throws IOException
299 	 *             Signals that an I/O exception has occurred.
300 	 */
301 	public static PrefixedResourceBundle getPrefixedResourceBundle(final String baseFileName, final Locale locale,
302 			final String defaultPrefix) throws IOException {
303 		ResourceBundle bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl(defaultPrefix));
304 		while (bundle != null && !(bundle instanceof PrefixedResourceBundle)) { // a
305 																				// prior
306 																				// call
307 																				// to
308 																				// getBundle
309 																				// might
310 																				// be
311 																				// called
312 																				// without
313 																				// the
314 																				// proper
315 																				// Control
316 																				// class.
317 																				// We
318 																				// need
319 																				// to
320 																				// clear
321 																				// the
322 																				// cache.
323 			ResourceBundle.clearCache();
324 			bundle = ResourceBundle.getBundle(baseFileName, locale, new PrefixedControl(defaultPrefix));
325 		}
326 		return (PrefixedResourceBundle) bundle;
327 	}
328 
329 	private final PrefixedProperties properties;
330 
331 	/**
332 	 * Instantiates a new prefixed resource bundle.
333 	 * 
334 	 * @param properties
335 	 *            the properties
336 	 */
337 	public PrefixedResourceBundle(final PrefixedProperties properties) {
338 		this.properties = properties;
339 	}
340 
341 	/**
342 	 * Gets the configured prefix.
343 	 * 
344 	 * @return the configured prefix
345 	 */
346 	public String getConfiguredPrefix() {
347 		return properties.getEffectivePrefix();
348 	}
349 
350 	/*
351 	 * (non-Javadoc)
352 	 * 
353 	 * @see java.util.ResourceBundle#getKeys()
354 	 */
355 	@SuppressWarnings("unchecked")
356 	@Override
357 	public Enumeration<String> getKeys() {
358 		return new ResouceBundleEnumeration(properties.stringPropertyNames().iterator());
359 	}
360 
361 	/**
362 	 * Gets the prefixed properties.
363 	 * 
364 	 * @return the prefixed properties
365 	 */
366 	public PrefixedProperties getPrefixedProperties() {
367 		return properties;
368 	}
369 
370 	/*
371 	 * (non-Javadoc)
372 	 * 
373 	 * @see java.util.ResourceBundle#handleGetObject(java.lang.String)
374 	 */
375 	@Override
376 	protected Object handleGetObject(final String key) {
377 		if (key == null) {
378 			throw new NullPointerException("The given key is null.");
379 		}
380 		return properties.get(key);
381 	}
382 
383 	public void setConfiguredPrefix(final String configuredPrefix) {
384 		properties.setLocalPrefix(configuredPrefix);
385 	}
386 
387 	/**
388 	 * Sets the default prefix.
389 	 * 
390 	 * @param prefix
391 	 *            the new default prefix
392 	 */
393 	public void setDefaultPrefix(final String prefix) {
394 		properties.setDefaultPrefix(prefix);
395 	}
396 }