001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: ResourceManager.java,v $
023   Revision 1.17  2004/05/05 21:22:45  markl
024   Comment header updates.
025
026   Revision 1.16  2003/01/19 09:42:04  markl
027   Throw exception if null or empty string passed in for resource name.
028
029   Revision 1.15  2002/08/11 09:57:21  markl
030   Version number change.
031
032   Revision 1.14  2002/08/11 09:48:54  markl
033   Added setter methods for resource paths.
034
035   Revision 1.13  2001/12/25 10:12:35  markl
036   Javadoc typo fix.
037
038   Revision 1.12  2001/08/28 20:28:21  markl
039   Fixes to defer access to Toolkit for situations where no X Display is
040   available.
041
042   Revision 1.11  2001/06/26 06:12:23  markl
043   Fixed javadoc.
044
045   Revision 1.10  2001/06/26 06:11:36  markl
046   Added getStream() method.
047
048   Revision 1.9  2001/03/12 03:16:50  markl
049   *** empty log message ***
050
051   Revision 1.8  1999/05/10 08:54:11  markl
052   Added color theme support.
053
054   Revision 1.7  1999/05/03 09:34:17  markl
055   Fixed javadoc comment.
056
057   Revision 1.6  1999/04/19 05:32:41  markl
058   Added LocaleData bundle support and bundle buffering.
059
060   Revision 1.5  1999/01/18 08:12:38  markl
061   fixed bugs in ResourceBundle methods
062
063   Revision 1.4  1999/01/17 08:32:57  markl
064   Added Locale awareness and new getResourceBundle() method.
065
066   Revision 1.3  1999/01/17 00:58:33  markl
067   Added ResourceBundle support
068
069   Revision 1.2  1999/01/10 03:55:05  markl
070   added GPL header & RCS tag
071   ----------------------------------------------------------------------------
072*/
073
074package kiwi.util;
075
076import java.awt.Image;
077import java.io.*;
078import java.util.*;
079import java.net.URL;
080import javax.swing.*;
081
082import kiwi.ui.*;
083
084/** This class provides base functionality for a resource manager; it includes
085  * support for the caching of images and sounds, and provides convenience
086  * methods for retrieving other types of resources. All resources are
087  * retrieved relative to an <i>anchor class</i>. The resource manager assumes
088  * that images will be within an "images" directory, textures within a
089  * "textures" directory, sounds within a "sounds" directory, URL-based
090  * references within an "html" directory, properties within a "properties"
091  * directory, resource bundles within a "locale" directory, and color theme
092  * definitions within a "themes" directory. All of these paths, however, are
093  * configurable.
094  * <p>
095  * The Kiwi library includes a resource library of its own; the resources
096  * within the library are accessible through the internal
097  * <code>ResourceManager</code>, a reference to which may be obtained via
098  * a call to <code>kiwi.util.KiwiUtils.getResourceManager()</code>. Links to
099  * index files of some of the resources are listed below:
100  * <p><ul>
101  * <li><a href="../../images_index.html">images</a>
102  * <li><a href="../../textures_index.html">textures</a>
103  * <li><a href="../../locale_index.html">resource bundles</a>
104  * <li><a href="../../sounds_index.html">sounds</a>
105  * </ul>
106  *
107  * @see kiwi.util.ResourceLoader
108  *
109  * @author Mark Lindner
110  */
111
112public class ResourceManager
113  {
114  private Hashtable images, textures, sounds, icons, bundles;
115  private ResourceLoader loader;
116  private static ResourceManager kiwiResourceManager = null;
117  private static final String respathSeparator = "/";
118  /** The default base path for images. */
119  public static final String IMAGE_PATH = "images";
120  /** The default base path for audio clips. */
121  public static final String SOUND_PATH = "sounds";
122  /** The default base path for HTML documents. */
123  public static final String HTML_PATH = "html";
124  /** The default base path for color themes. */
125  public static final String THEME_PATH = "themes";
126  /** The default base path for textures. */
127  public static final String TEXTURE_PATH = "textures";
128  /** The default base path for property files. */
129  public static final String PROPERTY_PATH = "properties";
130  /** The default base path for message bundles. */
131  public static final String RESBUNDLE_PATH = "locale";
132  /** The file extension for resource bundles. */
133  public static final String RESBUNDLE_EXT = ".msg";
134  /** The file extension for color themes. */
135  public static final String THEME_EXT = ".thm";
136
137  /** The base path for images. */
138  protected String imagePath;
139  /** The base path for audio clips. */
140  protected String soundPath;
141  /** The base path for HTML documents. */
142  protected String htmlPath;
143  /** The base path for color themes. */
144  protected String themePath;
145  /** The base path for textures. */
146  protected String texturePath;
147  /** The base path for property files. */
148  protected String propertyPath;
149  /** The base path for message bundles. */
150  protected String resbundlePath;
151  
152  /** Construct a new <code>ResourceManager</code>.
153    *
154    * @param clazz The resource anchor class.
155    */
156
157  public ResourceManager(Class clazz)
158    {
159    loader = new ResourceLoader(clazz);
160    images = new Hashtable();
161    icons = new Hashtable();
162    textures = new Hashtable();
163    sounds = new Hashtable();
164    bundles = new Hashtable();
165
166    setImagePath(IMAGE_PATH);
167    setSoundPath(SOUND_PATH);
168    setHTMLPath(HTML_PATH);
169    setThemePath(THEME_PATH);
170    setTexturePath(TEXTURE_PATH);
171    setPropertyPath(PROPERTY_PATH);
172    setResourceBundlePath(RESBUNDLE_PATH);
173    }
174
175  /*
176   */
177
178  private String _checkPath(String path)
179    {
180    if(path == null)
181      return(respathSeparator);
182    
183    return(path.endsWith(respathSeparator) ? path
184           : (path + respathSeparator));
185    }
186  
187  /** Set an alternate path (relative to the anchor class) for image and icon
188   * resources.
189   *
190   * @param path The new resource path.
191   * @since Kiwi 1.3.4
192   */
193
194  public void setImagePath(String path)
195    {
196    imagePath = _checkPath(path);
197    }
198
199  /** Set an alternate path (relative to the anchor class) for audio clip
200   * resources.
201   *
202   * @param path The new resource path.
203   * @since Kiwi 1.3.4
204   */
205
206  public void setSoundPath(String path)
207    {
208    soundPath = _checkPath(path);
209    }
210
211  /** Set an alternate path (relative to the anchor class) for HTML document
212   * resources.
213   *
214   * @param path The new resource path.
215   * @since Kiwi 1.3.4
216   */
217
218  public void setHTMLPath(String path)
219    {
220    htmlPath = _checkPath(path);
221    }
222
223  /** Set an alternate path (relative to the anchor class) for theme
224   * resources.
225   *
226   * @param path The new resource path.
227   * @since Kiwi 1.3.4
228   */
229
230  public void setThemePath(String path)
231    {
232    themePath = _checkPath(path);
233    }
234
235  /** Set an alternate path (relative to the anchor class) for texture
236   * resources.
237   *
238   * @param path The new resource path.
239   * @since Kiwi 1.3.4
240   */
241
242  public void setTexturePath(String path)
243    {
244    texturePath = _checkPath(path);
245    }
246
247  /** Set an alternate path (relative to the anchor class) for property
248   * resources.
249   *
250   * @param path The new resource path.
251   * @since Kiwi 1.3.4
252   */
253
254  public void setPropertyPath(String path)
255    {
256    propertyPath = _checkPath(path);
257    }
258
259  /** Set an alternate path (relative to the anchor class) for message bundle
260   * resources.
261   *
262   * @param path The new resource path.
263   * @since Kiwi 1.3.4
264   */
265
266  public void setResourceBundlePath(String path)
267    {
268    resbundlePath = _checkPath(path);
269    }
270  
271  /** Get a reference to the internal Kiwi resource manager.
272    */
273
274  public static ResourceManager getKiwiResourceManager()
275    {
276    if(kiwiResourceManager == null)
277      kiwiResourceManager = new ResourceManager(kiwi.ResourceAnchor.class);
278
279    return(kiwiResourceManager);
280    }
281
282  /** Clear the image resource cache. */
283  
284  public void clearImageCache()
285    {
286    images.clear();
287    }
288
289  /** Clear the icon resource cache. */
290  
291  public void clearIconCache()
292    {
293    icons.clear();
294    }
295
296  /** Clear the texture resource cache. */
297  
298  public void clearTextureCache()
299    {
300    textures.clear();
301    }
302
303  /** Clear the audio clip resource cache. */
304  
305  public void clearAudioClipCache()
306    {
307    sounds.clear();
308    }
309  
310  /** Clear the resource bundle cache. */
311  
312  public void clearResourceBundleCache()
313    {
314    bundles.clear();
315    }
316  
317  /** Retrieve an internal <code>Icon</code> resource. This is a convenience
318    * method that makes a call to <code>getImage()</code> and then wraps the
319    * result in a Swing <code>ImageIcon</code> object.
320    *
321    * @param name The name of the resource.
322    * @return An <code>Icon</code> for the specified image. If an icon for this
323    * image has previously been constructed, the cached copy is returned.
324    *
325    * @exception kiwi.util.ResourceNotFoundException If the resource was not
326    * found.
327    * @see javax.swing.ImageIcon
328    */
329
330  public Icon getIcon(String name)
331    {
332    Icon icon = (Icon)icons.get(name);
333    if(icon != null) return(icon);
334
335    icon = new ImageIcon(getImage(name));
336    icons.put(name, icon);
337
338    return(icon);
339    }
340
341  /** Retrieve an internal <code>Image</code> resource. If the named image has
342    * previously been loaded, a cached copy is returned.
343    *
344    * @param name The name of the resource.
345    * @return The <code>Image</code> object representing the resource.
346    * @exception kiwi.util.ResourceNotFoundException If the resource was not
347    * found.
348    */
349  
350  public Image getImage(String name)
351    {
352    checkResourceName(name);
353    
354    Image image = (Image)images.get(name);
355    if(image != null) return(image);
356
357    String path = imagePath + name;
358    image = loader.getResourceAsImage(path);
359    if(image == null)
360      throw(new ResourceNotFoundException(path));
361    images.put(name, image);
362
363    return(image);
364    }
365  
366  /** Retrieve an internal texture resource. If the named texture has
367    * previously been loaded, a cached copy is returned.
368    *
369    * @param name The name of the resource.
370    * @return The <code>Image</code> object representing the resource.
371    * @exception kiwi.util.ResourceNotFoundException If the resource was not
372    * found.
373    */
374
375  public Image getTexture(String name)
376    {
377    checkResourceName(name);
378    
379    Image image = (Image)textures.get(name);
380    if(image != null) return(image);
381
382    String path = texturePath + name;
383    image = loader.getResourceAsImage(path);
384    if(image == null)
385      throw(new ResourceNotFoundException(path));
386    textures.put(name, image);
387
388    return(image);
389    }
390
391  /** Retrieve an internal <code>URL</code> resource.
392    *
393    * @param name The name of the resource.
394    * @return A URL for the resource.
395    * @exception kiwi.util.ResourceNotFoundException If the resource was not
396    * found.
397    */
398
399  public URL getURL(String name)
400    {
401    checkResourceName(name);
402    
403    String path = htmlPath + name;
404    URL url = loader.getResourceAsURL(path);
405    if(url == null)
406      throw(new ResourceNotFoundException(path));
407    
408    return(url);
409    }
410
411  /** Retrieve an internal <code>AudioClip</code> resource. If the named sound
412    * has previously been loaded, a cached copy is returned.
413    *
414    * @param name The name of the resource.
415    * @return The <code>AudioClip</code> object representing the resource.
416    * @exception kiwi.util.ResourceNotFoundException If the resource was not
417    * found.
418    */
419
420  public AudioClip getSound(String name)
421    {
422    checkResourceName(name);
423    
424    String path = soundPath + name;
425    AudioClip clip = loader.getResourceAsAudioClip(soundPath + name);
426    if(clip == null)
427      throw(new ResourceNotFoundException(path));
428    
429    return(clip);
430    }
431
432  /** Get a reference to a <code>Properties</code> resource.
433    *
434    * @param name The name of the resource.
435    * @return The <code>Properties</code> object representing the resource.
436    * @exception kiwi.util.ResourceNotFoundException If the resource was not
437    * found.
438    */
439
440  public Properties getProperties(String name)
441    {
442    checkResourceName(name);
443    
444    Properties props = null;
445    String path = propertyPath + name;
446    
447    try
448      {
449      props = loader.getResourceAsProperties(path);
450      }
451    catch(IOException ex) { }
452    
453    if(props == null)
454      throw(new ResourceNotFoundException(path));
455
456    return(props);
457    }
458
459  /** Get a reference to a <code>LocaleData</code> object for the default
460   * locale. The locale naming convention
461   * <i>basename_language_country_variant</i> is
462   * supported; a search is performed starting with the most specific name
463   * and ending with the most generic.
464   *
465   * @param name The name of the resource; this should be the base name of
466   * the resource bundle; the appropriate locale country, language, and
467   * variant codes and the ".msg" extension will be automatically
468   * appended to the name.
469   * @return The <code>LocaleData</code> object representing the resource.
470   * If the resource bundle has been previously loaded, a cached copy is
471   * returned.
472   * @exception kiwi.util.ResourceNotFoundException If the resource was not
473   * found.
474   */
475
476  public LocaleData getResourceBundle(String name)
477    throws ResourceNotFoundException
478    {    
479    return(getResourceBundle(name, Locale.getDefault()));
480    }
481  
482  /** Get a reference to a <code>LocaleData</code> object for a specified
483   * locale. The locale naming convention
484   * <i>basename_language_country_variant</i> is
485   * supported; a search is performed starting with the most specific name
486   * and ending with the most generic. If the resource bundle has been
487   * previously loaded, a cached copy is returned.
488   *
489   * @param name The name of the resource; this should be the base name of
490   * the resource bundle; the appropriate locale contry, language, and
491   * variant codes and the ".msg" extension will be automatically
492   * appended to the name.
493   * @param locale The locale for the resource.
494   * @return The <code>LocaleData</code> object representing the resource.
495   * @exception kiwi.util.ResourceNotFoundException If the resource was not
496   * found.
497   */
498
499  public LocaleData getResourceBundle(String name, Locale locale)
500    throws ResourceNotFoundException
501    {
502    checkResourceName(name);
503    
504    String path = resbundlePath + name, cpath = null;
505    LocaleData bundle = null;
506
507    Stack paths = new Stack();
508    paths.push(path + RESBUNDLE_EXT);
509    path += "_" + locale.getLanguage();
510    paths.push(path + RESBUNDLE_EXT);
511    String country = locale.getCountry();
512    if((country != null) && (country.length() > 0))
513      {
514      path += "_" + country;
515      paths.push(path + RESBUNDLE_EXT);
516      String variant = locale.getVariant();
517      if((variant != null) && (variant.length() > 0))
518        {
519        path += "_" + variant;
520        paths.push(variant + RESBUNDLE_EXT);
521        }
522      }
523
524    while(!paths.empty())
525      {
526      cpath = (String)paths.pop();
527
528      bundle = (LocaleData)bundles.get(cpath);
529      if(bundle == null)
530        {
531        try
532          {
533          InputStream is = loader.getResourceAsStream(cpath);
534          bundle = new LocaleData(is);
535          bundles.put(cpath, bundle);
536          }
537        catch(IOException ex)
538          {
539          bundle = null;
540          }
541        }
542
543      if(bundle != null) break;
544      }
545      
546    if(bundle == null)
547      throw(new ResourceNotFoundException(cpath));
548
549    return(bundle);
550    }
551
552  /** Get a reference to a <code>ColorTheme</code> resource.
553    *
554    * @param name The name of the resource; this should be the base name of
555    * the color theme; the ".thm" extension will be automatically
556    * appended to the name.
557    * @return The <code>ColorTheme</code> object representing the resource.
558    * @exception kiwi.util.ResourceNotFoundException If the resource was not
559    * found.
560    */
561
562  public ColorTheme getTheme(String name)
563    {
564    checkResourceName(name);
565    
566    String path = themePath + name + THEME_EXT;
567    ColorTheme theme = null;
568    
569    try
570      {
571      InputStream is = loader.getResourceAsStream(path);
572      Config cfg = new Config();
573      cfg.load(is);
574      theme = new ColorTheme(cfg);
575      }
576    catch(IOException ex)
577      {
578      throw(new ResourceNotFoundException(path));
579      }
580
581    return(theme);
582    }
583
584  /** Get a stream to a resource.
585   *
586   * @param name The name of the resource.
587   * @return An <code>InputStream</code> from which the resource can be read.
588   * @exception kiwi.util.ResourceNotFoundException If the resource was not
589   * found.
590   * @since Kiwi 1.3
591   */
592
593  public InputStream getStream(String name) throws ResourceNotFoundException
594    {
595    checkResourceName(name);
596    
597    InputStream is;
598
599    try
600      {
601      is = loader.getResourceAsStream(name);
602      }
603    catch(IOException ex)
604      {
605      throw(new ResourceNotFoundException(name));
606      }
607    
608    return(is);
609    }
610
611  /*
612   */
613
614  private void checkResourceName(String name) throws IllegalArgumentException
615    {
616    if((name == null) || name.equals(""))
617      throw(new IllegalArgumentException("Null or empty resource name"));
618    }
619  
620  }
621
622/* end of source file */