001 /*
002 * $Id: ResourcesBase.java 354330 2005-12-06 06:05:19Z niallp $
003 * $Revision: 354330 $
004 * $Date: 2005-12-06 06:05:19 +0000 (Tue, 06 Dec 2005) $
005 *
006 * ====================================================================
007 *
008 * Copyright 2003-2005 The Apache Software Foundation
009 *
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 * http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 *
022 */
023
024 package org.apache.commons.resources.impl;
025
026 import java.io.ByteArrayInputStream;
027 import java.io.InputStream;
028 import java.io.InputStreamReader;
029 import java.io.Reader;
030 import java.io.StringReader;
031 import java.io.IOException;
032 import java.util.Iterator;
033 import java.util.Locale;
034
035 import org.apache.commons.resources.Resources;
036 import org.apache.commons.resources.ResourcesException;
037 import org.apache.commons.resources.ResourcesKeyException; // for javadoc/checkstyle
038
039 /**
040 * <p>Convenience base class for
041 * {@link org.apache.commons.resources.Resources} implementations.</p>
042 *
043 * <p>Default implementations of the content retrieval methods are provided
044 * for all methods except <code>getObject()</code>. The default methods for
045 * the other content retrieval methods are coded in terms of
046 * <code>getString()</code>, on the assumption that most uses of
047 * {@link org.apache.commons.resources.Resources} are of this type.
048 * However, they can be easily overridden as needed.</p>
049 *
050 * @see org.apache.commons.resources.impl.CollectionResourcesBase
051 * @see org.apache.commons.resources.impl.JDBCResources
052 * @see org.apache.commons.resources.impl.PropertyResources
053 * @see org.apache.commons.resources.impl.ResourceBundleResources
054 * @see org.apache.commons.resources.impl.WebappPropertyResources
055 * @see org.apache.commons.resources.impl.WebappXMLResources
056 * @see org.apache.commons.resources.impl.XMLResources
057 */
058 public abstract class ResourcesBase implements Resources {
059
060
061 // ----------------------------------------------------------- Constructors
062
063
064 /**
065 * <p>Create a new {@link org.apache.commons.resources.Resources} instance
066 * with no name.</p>
067 */
068 public ResourcesBase() {
069
070 this(null);
071
072 }
073
074
075 /**
076 * <p>Create a new {@link org.apache.commons.resources.Resources} instance
077 * with the specified logical name.</p>
078 *
079 * @param name Logical name of the new instance
080 */
081 public ResourcesBase(String name) {
082
083 this.name = name;
084
085 }
086
087
088 // ----------------------------------------------------- Instance Variables
089
090
091 /**
092 * <p>The logical name of this {@link org.apache.commons.resources.Resources}
093 * instance.</p>
094 */
095 private String name = null;
096
097
098 /**
099 * <p>Flag indicating whether resource getter methods should return
100 * <code>null</code> (instead of throwing an exception) on invalid
101 * key values.</p>
102 */
103 private boolean returnNull = true;
104
105 /**
106 * Buffer size for creating char and byte arrays.
107 */
108 private int bufferSize = 500;
109
110 // ------------------------------------------------------ Lifecycle Methods
111
112
113 /**
114 * <p>This must be called to initialize the data content of this
115 * {@link org.apache.commons.resources.Resources} instance, before
116 * any of the <code>getXxx()</code> methods are called.</p>
117 *
118 * <p>The default implementation does nothing.</p>
119 *
120 * @exception ResourcesException if an error occurs during initialization
121 */
122 public void init() {
123
124 // The default implementation does nothing
125
126 }
127
128
129 /**
130 * <p>This method must be called when the manager of this resource
131 * decides that it's no longer needed. After this method is called,
132 * no further calls to any of the <code>getXxx()</code> methods are
133 * allowed.</p>
134 *
135 * <p>The default implementation does nothing.</p>
136 *
137 * @exception ResourcesException if an error occurs during finalization
138 */
139 public void destroy() {
140
141 // The default implementation does nothing
142
143 }
144
145
146 // ------------------------------------------------------------- Properties
147
148
149 /**
150 * <p>Return an <code>Iterator</code> over the defined keys in this
151 * {@link org.apache.commons.resources.Resources} instance.</p>
152 *
153 * @return The keys contained in this resources instance.
154 */
155 public abstract Iterator getKeys();
156
157
158 /**
159 * <p>Return the logical name of this {@link org.apache.commons.resources.Resources}
160 * instance.</p>
161 *
162 * @return The name of this resources instance.
163 */
164 public String getName() {
165
166 return (this.name);
167
168 }
169
170
171 /**
172 * <p>Return <code>true</code> if resource getter methods will return
173 * <code>null</code> instead of throwing an exception on invalid
174 * key values.</p>
175 * @return 'true' if null is returned for invalid key values.
176 */
177 public boolean isReturnNull() {
178
179 return (this.returnNull);
180
181 }
182
183
184 /**
185 * <p>Set a flag determining whether resource getter methods should
186 * return <code>null</code> instead of throwing an exception on
187 * invalid key values.</p>
188 *
189 * @param returnNull The new flag value
190 */
191 public void setReturnNull(boolean returnNull) {
192
193 this.returnNull = returnNull;
194
195 }
196
197
198 /**
199 * <p>Return the size of the buffer to use when converting
200 * InputStream or Reader objects.</p>
201 *
202 * @return The buffer size.
203 */
204 public int getBufferSize() {
205
206 return bufferSize;
207
208 }
209
210 /**
211 * <p>Set the size of the buffer to use when converting
212 * InputStream or Reader objects.</p>
213 *
214 * @param bufferSize The buffer size.
215 */
216 public void setBufferSize(int bufferSize) {
217
218 this.bufferSize = bufferSize;
219
220 }
221
222
223 // ---------------------------------------------- Content Retrieval Methods
224
225
226 /**
227 * <p>Return the content for the specified <code>key</code> as a
228 * byte array, localized based on the specified <code>locale</code>.
229 * </p>
230 *
231 * <p>The default implementation calls <code>getString()</code> and
232 * converts the value to a byte array.</p>
233 *
234 * @param key Identifier for the requested content
235 * @param locale Locale with which to localize retrieval,
236 * or <code>null</code> for the default Locale
237 * @return content for a specified key.
238 *
239 * @exception ResourcesException if an error occurs retrieving or
240 * returning the requested content
241 * @exception ResourcesKeyException if the no value for the specified
242 * key was found, and <code>isReturnNull()</code> returns
243 * <code>false</code>
244 */
245 public byte[] getBytes(String key, Locale locale) {
246
247 Object value = getObject(key, locale);
248 if (value == null) {
249 return (null);
250 } else if (value instanceof String) {
251 return ((String)value).getBytes();
252 } else if (value instanceof Reader) {
253 char[] chars = getChars((Reader)value);
254 if (chars == null) {
255 return (byte[])checkThrow(key);
256 }
257 return new String(chars).getBytes();
258 } else if (value instanceof InputStream) {
259 byte[] bytes = getBytes((InputStream)value);
260 if (bytes == null) {
261 return (byte[])checkThrow(key);
262 }
263 return bytes;
264 } else if (value instanceof byte[]) {
265 return (byte[])value;
266 } else {
267 return value.toString().getBytes();
268 }
269
270 }
271
272
273 /**
274 * <p>Return the content for the specified <code>key</code> as an
275 * InputStream, localized based on the specified <code>locale</code>.
276 * </p>
277 *
278 * <p>The default implementation calls <code>getsBytes()</code>
279 * and returns an input stream over the resulting byte array.</p>
280 *
281 * @param key Identifier for the requested content
282 * @param locale Locale with which to localize retrieval,
283 * or <code>null</code> for the default Locale
284 * @return content for a specified key.
285 *
286 * @exception ResourcesException if an error occurs retrieving or
287 * returning the requested content
288 * @exception ResourcesKeyException if the no value for the specified
289 * key was found, and <code>isReturnNull()</code> returns
290 * <code>false</code>
291 */
292 public InputStream getInputStream(String key, Locale locale) {
293
294 Object value = getObject(key, locale);
295 if (value == null) {
296 return (null);
297 } else if (value instanceof String) {
298 byte[] bytes = ((String)value).getBytes();
299 return new ByteArrayInputStream(bytes);
300 } else if (value instanceof Reader) {
301 char[] chars = getChars((Reader)value);
302 if (chars == null) {
303 return (InputStream)checkThrow(key);
304 }
305 byte[] bytes = (new String(chars)).getBytes();
306 return new ByteArrayInputStream(bytes);
307 } else if (value instanceof InputStream) {
308 return (InputStream)value;
309 } else if (value instanceof byte[]) {
310 return new ByteArrayInputStream((byte[])value);
311 } else {
312 byte[] bytes = value.toString().getBytes();
313 return new ByteArrayInputStream(bytes);
314 }
315
316 }
317
318
319 /**
320 * <p>Return the content for the specified <code>key</code> as an
321 * Object, localized based on the specified <code>locale</code>.
322 * </p>
323 *
324 * <p>There is no default implementation of this method. Concrete
325 * subclasses must provide such an implementation.</p>
326 *
327 * @param key Identifier for the requested content
328 * @param locale Locale with which to localize retrieval,
329 * or <code>null</code> for the default Locale
330 * @return content for a specified key.
331 *
332 * @exception ResourcesException if an error occurs retrieving or
333 * returning the requested content
334 * @exception ResourcesKeyException if the no value for the specified
335 * key was found, and <code>isReturnNull()</code> returns
336 * <code>false</code>
337 */
338 public abstract Object getObject(String key, Locale locale);
339
340
341 /**
342 * <p>Return the content for the specified <code>key</code> as a
343 * Reader, localized based on the specified <code>locale</code>.
344 * </p>
345 *
346 * <p>The default implementation calls <code>getString()</code>
347 * and returns a reader over the resulting characters.</p>
348 *
349 * @param key Identifier for the requested content
350 * @param locale Locale with which to localize retrieval,
351 * or <code>null</code> for the default Locale
352 * @return content for a specified key.
353 *
354 * @exception ResourcesException if an error occurs retrieving or
355 * returning the requested content
356 * @exception ResourcesKeyException if the no value for the specified
357 * key was found, and <code>isReturnNull()</code> returns
358 * <code>false</code>
359 */
360 public Reader getReader(String key, Locale locale) {
361
362 Object value = getObject(key, locale);
363 if (value == null) {
364 return (null);
365 } else if (value instanceof String) {
366 return new StringReader((String)value);
367 } else if (value instanceof Reader) {
368 return (Reader)value;
369 } else if (value instanceof InputStream) {
370 return new InputStreamReader((InputStream)value);
371 } else if (value instanceof byte[]) {
372 InputStream bais = new ByteArrayInputStream((byte[])value);
373 return new InputStreamReader(bais);
374 } else {
375 return new StringReader(value.toString());
376 }
377
378 }
379
380
381 /**
382 * <p>Return the content for the specified <code>key</code> as a
383 * String, localized based on the specified <code>locale</code>.
384 * </p>
385 *
386 * <p>The default implementation calls <code>getObject()</code>
387 * and converts the result to a String if necessary.</p>
388 *
389 * @param key Identifier for the requested content
390 * @param locale Locale with which to localize retrieval,
391 * or <code>null</code> for the default Locale
392 * @return content for a specified key.
393 *
394 * @exception ResourcesException if an error occurs retrieving or
395 * returning the requested content
396 * @exception ResourcesKeyException if the no value for the specified
397 * key was found, and <code>isReturnNull()</code> returns
398 * <code>false</code>
399 */
400 public String getString(String key, Locale locale) {
401
402 Object value = getObject(key, locale);
403 if (value == null) {
404 return (null);
405 } else if (value instanceof String) {
406 return ((String) value);
407 } else if (value instanceof Reader) {
408 char[] chars = getChars((Reader)value);
409 if (chars == null) {
410 return (String)checkThrow(key);
411 }
412 return new String(chars);
413 } else if (value instanceof InputStream) {
414 byte[] bytes = getBytes((InputStream)value);
415 if (bytes == null) {
416 return (String)checkThrow(key);
417 }
418 return new String(bytes);
419 } else if (value instanceof byte[]) {
420 return new String((byte[])value);
421 } else {
422 return (value.toString());
423 }
424
425 }
426
427
428 /**
429 * Convert a Reader to a char array.
430 */
431 private char[] getChars(Reader reader) {
432
433 char[] array = null;
434 int arrayLth = 0;
435 try {
436 while (true) {
437 char[] buffer = new char[bufferSize];
438 int bufferLth = reader.read(buffer);
439 if (bufferLth < 0) {
440 break;
441 }
442 if (array == null && bufferLth == buffer.length) {
443 array = buffer;
444 } else {
445 char[] newArray = new char[arrayLth + bufferLth];
446 if (array != null) {
447 System.arraycopy(array, 0, newArray, 0, arrayLth);
448 }
449 System.arraycopy(buffer, 0, newArray, arrayLth, bufferLth);
450 array = newArray;
451 }
452 arrayLth = array.length;
453 }
454 } catch(IOException e) {
455 throw new ResourcesException("Error reading Reader", e);
456 }
457
458 return array;
459
460 }
461
462 /**
463 * Convert an InputStream to a byte array.
464 */
465 private byte[] getBytes(InputStream inputStream) {
466
467 byte[] array = null;
468 int arrayLth = 0;
469 try {
470 while (true) {
471 byte[] buffer = new byte[bufferSize];
472 int bufferLth = inputStream.read(buffer);
473 if (bufferLth < 0) {
474 break;
475 }
476 if (array == null && bufferLth == buffer.length) {
477 array = buffer;
478 } else {
479 byte[] newArray = new byte[arrayLth + bufferLth];
480 if (array != null) {
481 System.arraycopy(array, 0, newArray, 0, arrayLth);
482 }
483 System.arraycopy(buffer, 0, newArray, arrayLth, bufferLth);
484 array = newArray;
485 }
486 arrayLth = array.length;
487 }
488 } catch(IOException e) {
489 throw new ResourcesException("Error reading InputStream", e);
490 }
491 return array;
492
493 }
494
495 /**
496 * Check whether this Resources instance is
497 * configured to return null or throw a ResourcesKeyException.
498 * @param key Identifier for the requested content
499 * @return 'null' if returnNull is 'true' otherwise throws
500 * a ResourcesKeyException.
501 */
502 private Object checkThrow(String key) {
503 if (isReturnNull()) {
504 return (null);
505 } else {
506 throw new ResourcesKeyException(key);
507 }
508 }
509
510 }