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    }