Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/dinopolis/util/resource/FileResources.java


1   /***********************************************************************
2    * @(#)$RCSfile: FileResources.java,v $   $Revision: 1.7 $ $Date: 2003/11/18 11:34:08 $
3    *
4    * Copyright (c) 2001 IICM, Graz University of Technology
5    * Inffeldgasse 16c, A-8010 Graz, Austria.
6    * 
7    * This program is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License (LGPL)
9    * as published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU Lesser General Public License for more details.
16   * 
17   * You should have received a copy of the GNU Lesser General Public 
18   * License along with this program; if not, write to the
19   * Free Software Foundation, Inc., 
20   * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21   ***********************************************************************/
22  
23  
24  package org.dinopolis.util.resource;
25  
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileNotFoundException;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.net.MalformedURLException;
32  import java.net.URL;
33  import java.util.Enumeration;
34  import java.util.HashSet;
35  import java.util.Locale;
36  import java.util.MissingResourceException;
37  import java.util.Properties;
38  import java.util.ResourceBundle;
39  import java.util.Vector;
40  import java.util.WeakHashMap;
41  
42  //----------------------------------------------------------------------
43  /**
44   * This class provides the functionality to read resources from a
45   * system or user resource file. The way that this is done is: It
46   * looks for a resource file within the users home directory and if a
47   * proper file is found, it looks for the specific proporty. If no
48   * file or the specific property was not found, the system resource
49   * file is being asked for the property.<p>
50   *
51   * Stored resources can not only be requested as <code>Strings</code>,
52   * but also in many different other types, like <code>integers</code>,
53   * <code>StringArrays</code>, <code>booleans</code>, <code>Colors</code>,
54   * <code>Icons</code> and others.<p>
55   *
56   * Resource files contain key/value pairs. The keys uniquely
57   * identify a locale-specific object in the bundle. Here's an
58   * example resource file that contains two key/value pairs:
59   * <blockquote>
60   * <pre>
61   * my_app.dimension.width=300
62   * my_app.dimension.height=200
63   * </pre>
64   * </blockquote>
65   *
66   * Keys are always <code>String</code>s.
67   * In this example, the keys are <code>my_app.dimension.width</code>
68   * and <code>my_app.dimension.height</code>.
69   *
70   * In the above example, the values
71   * are also <code>String</code>s--<code>200</code> and <code>300</code>--but
72   * they may also be interpreted as integer values using the method
73   * {@link #getInt}.<p>
74   *
75   * The single argumented getter methods all require the key as an
76   * argument and return the object if found. If the object is not
77   * found, the getter method throws a
78   * <code>MissingResourceException</code>. To avoid this behavior, it
79   * is also possible to use the corresponding methods that take two
80   * arguments, the key and the default value. If no property was bound
81   * under the given key, the default value will be returned instead of
82   * throwing a <code>MissingResourceException</code>.
83   *
84   * @author Dieter Freismuth
85   * @version $Revision: 1.7 $
86   */
87  
88  public class FileResources extends AbstractResources
89  {
90    /** the property filename extension */
91    protected final static String PROPERTY_EXTENSION = ".properties";
92  
93    /** the title suffix */
94    protected final static String TITLE_SUFFIX = ".title";
95  
96    /** the description suffix */
97    protected final static String DESCRIPTION_SUFFIX = ".description";
98  
99    /** the possible values suffix */
100   protected final static String POSSIBLE_VALUES_SUFFIX = ".possible_values";
101 
102   /** the type suffix */
103   protected final static String TYPE_SUFFIX = ".type";
104 
105   /** key - value seperators */
106   protected static final String KEY_VALUE_SEPARATORS = "=: \t\r\n\f";
107 
108   /** whitespace charakters */
109   protected final static String WHITE_SPACE_CHARS = " \t\r\n\f";
110 
111   /** the resource bundle */
112   protected ResourceBundle system_bundle_;
113 
114   /** the users private resource bundle */
115   protected Properties user_properties_;
116 
117   /** the file that holds the users resources */
118   protected File user_resource_file_;
119 
120   /** the system resource base dir */
121   protected String user_resource_base_name_;
122 
123   /** the user resource base dir */
124   protected String system_resource_base_name_;
125 
126   /** the mapping holding all requested FileResources */
127   protected static WeakHashMap mapping_ = new WeakHashMap();
128 
129   //----------------------------------------------------------------------
130   /**
131    * Creates a new FileResources class.
132    *
133    * @param user_resource_file the resource file of the user.
134    * @param user_bundle the user resource bundle.
135    * @param user_resource_base_name the file name of the user_bundles
136    * directory.
137    * @param system_bundle the system resource bundle.
138    * @param system_resource_base_name the file name of the
139    * system_bundles directory.
140    */
141 
142   protected FileResources(File user_resource_file,
143                           Properties user_bundle, 
144                           String user_resource_base_name,
145                           ResourceBundle system_bundle,
146                           String system_resource_base_name)
147   {
148     user_resource_file_ = user_resource_file;
149     user_properties_ = user_bundle;
150     if (user_properties_ == null)
151       user_properties_ = new Properties();
152     user_resource_base_name_ = user_resource_base_name;
153     system_bundle_ = system_bundle;
154     system_resource_base_name_ = system_resource_base_name;
155   }
156 
157   //----------------------------------------------------------------------
158   /**
159    * Get the appropriate FileResources for the given base_name.
160    *
161    * @param base_name the base name of the resource bundle.
162    * @return the Resource.
163    * @exception MissingResourceException if the system resource file
164    * could not be located. 
165    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
166    */
167  
168   public static FileResources getResources(String base_name)
169     throws MissingResourceException
170   {
171     return(getResources(null, base_name, base_name, null));
172   }
173 
174   //----------------------------------------------------------------------
175   /**
176    * Get the appropriate FileResources for the given base_name and
177    * searches the system bundle in the callers package name.
178    * See also
179    * {@link #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)} 
180    *
181    * @param caller the caller, to search the system bundle for.
182    * @param base_name the base name of the resource bundle.
183    * @return the Resource.
184    * @exception MissingResourceException if the system resource file
185    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
186    */
187  
188   public static FileResources getResources(Class caller, 
189                                            String base_name)
190     throws MissingResourceException
191   {
192     return(getResources(caller, base_name, base_name, null));
193   }
194 
195   //----------------------------------------------------------------------
196   /**
197    * Get the appropriate FileResources for the given base_name and the
198    * given locale.
199    *
200    * @param base_name the base name of the resource bundle.
201    * @param locale the locale.
202    * @return the Resource.
203    * @exception MissingResourceException if the system resource file
204    * could not be located. 
205    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
206    */
207 
208   public static FileResources getResources(String base_name,
209                                            Locale locale)
210     throws MissingResourceException
211   {
212     return(getResources(null, base_name, base_name, locale));
213   }
214 
215   //----------------------------------------------------------------------
216   /**
217    * Get the appropriate FileResources for the given base_name and locale
218    * and searches the system bundle in the callers package name. See
219    * also {@link
220    * #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)}  
221    *
222    * @param caller the caller, to search the system bundle for.
223    * @param base_name the base name of the resource bundle.
224    * @param locale the locale.
225    * @return the Resource.
226    * @exception MissingResourceException if the system resource file
227    * could not be located. 
228    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
229    */
230 
231   public static FileResources getResources(Class caller, 
232                                            String base_name,
233                                            Locale locale)
234     throws MissingResourceException
235   {
236     return(getResources(caller, base_name, base_name, locale));
237   }
238 
239 
240   //----------------------------------------------------------------------
241   /**
242    * Get the appropriate FileResources for the given base_name and the
243    * given dir_name. The dir_name specifies the directory name of the
244    * users resource file, relative to the users home directory.
245    *
246    * @param base_name the base name of the resource bundle.
247    * @param dir_name the name of the directory within the users homedir
248    * to look for a property file.
249    * @return the Resource.
250    * @exception MissingResourceException if the system resource file
251    * could not be located. 
252    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
253  */
254  
255   public static FileResources getResources(String base_name, 
256                                            String dir_name)
257     throws MissingResourceException
258   {
259     return(getResources(null, base_name, dir_name, null));
260   }
261 
262   //----------------------------------------------------------------------
263   /**
264    * Get the appropriate FileResources for the given base_name and the
265    * given dir_name and searches the system bundle in the callers
266    * package name. The dir_name specifies the directory name of the
267    * users resource file, relative to the users home directory.
268    *
269    * @param caller the caller, to search the system bundle for.
270    * @param base_name the base name of the resource bundle.
271    * @return the Resource.
272    * @param dir_name the name of the directory within the users homedir
273    * to look for a property file.
274    * @exception MissingResourceException if the system resource file
275    * could not be located. 
276    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
277    */
278  
279   public static FileResources getResources(Class caller, 
280                                            String base_name,
281                                            String dir_name)
282     throws MissingResourceException
283   {
284     return(getResources(caller, base_name, dir_name, null));
285   }
286 
287   //----------------------------------------------------------------------
288   /**
289    * Get the appropriate FileResources for the given base_name, the given
290    * dir_name and the given locale. The dir_name specifies the
291    * directory name of the users resource file, relative to the users
292    * home directory.
293    *
294    * @param base_name the base name of the resource bundle.
295    * @param locale the locale.
296    * @param dir_name the name of the directory within the users homedir
297    * to look for a property file.
298    * @return the Resource.
299    * @exception MissingResourceException if the system resource file
300    * could not be located. 
301    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
302    */
303 
304   public static FileResources getResources(String base_name, 
305                                            String dir_name,
306                                            Locale locale)
307     throws MissingResourceException
308   {
309     return(getResources(null, base_name, dir_name, locale));
310   }
311 
312   //----------------------------------------------------------------------
313   /**
314    * Get the appropriate ResourceBundle. The ResourceBundle consists
315    * of two parts, the system resources (for default settings) and
316    * user specific settings. Any given properties in the users
317    * resources will overwrite the system resources!  The users
318    * resources are searched in the following order:
319    * $user_home/<code>dir_name</code>/<code>base_name</code>.properties,
320    * $user_home/.<code>dir_name</code>/<code>base_name</code>.properties,
321    * $user_home/<code>dir_name</code>.toLowerCase()/<code>base_name</code>.properties,
322    * $user_home/.<code>dir_name</code>.toLowerCase()/<code>base_name</code>.properties<p>
323    * 
324    * The system resources are expected to be in the same package than
325    * the caller!<p>
326    *
327    * Example:<br>
328    * caller: <code>org.dinopolis.util.FileResources</code><br>
329    * base_name: <code>TestFileResources</code><br>
330    * dir_name: <code>test_resources</code><br>
331    * locale: <code>Locale.getDefault()</code><br>
332    * users home dir: ~dfreis<p>
333    *
334    * then, the users bundle is suspected to be:<br>
335    * "~dfreis/test_resources/TestFileResources.properties", or if not found:<br>
336    * "~dfreis/.test_resources/TestFileResources.properties"
337    *
338    * the system resources are expected to be in:<br>
339    * "org.dinopolis.util.FileResources.TestFileResources.properties"
340    * within your classpath!<p>
341    * 
342    * The sample code of the caller for the given example will look
343    * like this:
344    * <xmp>
345    * FileResources resources = FileResources.getResources(getClass(), "TestFileResources", 
346    *                                           "test_resources");
347    * </xmp>
348    *
349    * @param caller the caller, to search the system bundle for.
350    * @param base_name the base name of the resource bundle.
351    * @param dir_name the name of the directory within the users homedir
352    * to look for a property file.
353    * @param locale the locale.
354    * @return the Resource.
355    * @exception MissingResourceException if the system resource file
356    * could not be located.
357    */
358 
359   public static FileResources getResources(Class caller, 
360                                            String base_name,
361                                            String dir_name,
362                                            Locale locale)
363     throws MissingResourceException
364   {
365     return(getResources(caller,base_name,dir_name,locale,null));
366   }
367 
368   //----------------------------------------------------------------------
369   /**
370    * Get the appropriate ResourceBundle with the use of the given
371    * classloader.
372    *
373    * @param caller the caller, to search the system bundle for.
374    * @param base_name the base name of the resource bundle.
375    * @param dir_name the name of the directory within the users homedir
376    * to look for a property file.
377    * @param locale the locale.
378    * @param loader the class loader to use (if null, the class loader
379    * of the caller is used (or of the FileResource class, if call is null)).
380    * @return the Resource.
381    * @exception MissingResourceException if the system resource file
382    * could not be located.
383    * @see #getResources(java.lang.Class,java.lang.String,java.lang.String,java.util.Locale)
384    */
385    
386    public static FileResources getResources(Class caller, 
387                                             String base_name,
388                                             String dir_name,
389                                             Locale locale,
390                                             ClassLoader loader)
391     throws MissingResourceException
392   {
393     String system_resource_base_name =
394       getSystemResourceBaseName(caller);
395     String user_resource_base_name = getUserResourceBaseName(dir_name);
396     File user_resource_file = getUsersResourceFile(base_name, user_resource_base_name);
397     
398     String key = locale+":"+base_name+":"+system_resource_base_name+ 
399       ":"+user_resource_base_name; 
400     FileResources bound = (FileResources)mapping_.get(key);
401     if (bound == null)
402     {
403       if(loader == null)
404       {
405         if(caller == null)
406           loader = FileResources.class.getClassLoader();
407         else
408           loader = caller.getClassLoader();
409       }
410       bound = new FileResources(user_resource_file,
411                                 getUsersResourceBundle(user_resource_file),
412                                 user_resource_base_name,
413                                 getSystemResourceBundle(system_resource_base_name,
414                                                         base_name, locale, loader),
415                                 system_resource_base_name);
416       mapping_.put(key, bound);
417     }
418     return(bound);
419   }
420 
421   //----------------------------------------------------------------------
422   /**
423    * Returs the base name of the system resource bundle. The base name
424    * corresponds to the callers package name.
425    *
426    * @param caller the caller.
427    * @return the base name of the resource bundle.
428    */
429 
430   protected static String getSystemResourceBaseName(Class caller)
431   {
432     if (caller == null)
433       return("");
434     String caller_package = caller.getName();
435     int las_dot = caller_package.lastIndexOf('.');
436     if (las_dot > 0) 
437       caller_package = caller_package.substring(0, las_dot);
438     else
439       caller_package = "";
440     return(caller_package.replace('.','/'));
441   }
442 
443   //----------------------------------------------------------------------
444   /**
445    * Returns the system resource bundle.
446    *
447    * @param system_resource_base_dir he base directory of the system
448    * resource bundle.
449    * @param base_name he base name of the resource file.
450    * @param locale the locale (if null, the default locale is used.)
451    * @param loader the class loader to use.
452    * @return the system resource bundle if found.
453    * @exception MissingResourceException if the system resource file
454    * could not be located. 
455    */
456 
457   protected static ResourceBundle getSystemResourceBundle(String system_resource_base_dir,
458                                                           String base_name,
459                                                           Locale locale,
460                                                           ClassLoader loader)
461     throws MissingResourceException
462   {
463     String resource = system_resource_base_dir;
464     if (resource.length() > 0)
465       resource += ".";
466     resource += base_name;
467     if (locale != null)
468       return(ResourceBundle.getBundle(resource, locale,loader));
469     return(ResourceBundle.getBundle(resource,Locale.getDefault(),loader));
470   }
471 
472   //----------------------------------------------------------------------
473   /**
474    * Returns the path of the users resource bundle.
475    * The Resource bundle is searched in the following order:
476    * $user_home/<code>dir_name</code>,
477    * $user_home/.<code>dir_name</code>,
478    * $user_home/<code>dir_name</code>.toLowerCase(),
479    * $user_home/.<code>dir_name</code>.toLowerCase().
480    * 
481    * @param dir_name the name of the directory within the users homedir
482    * to look for a property file.
483    * @return the base name of the resource bundle, or null if no dir was
484    * found.
485    */
486 
487   protected static String getUserResourceBaseName(String dir_name)
488   {
489     File home_dir = new File(System.getProperty("user.home"));
490     File resource_dir = new File(home_dir, dir_name);
491     if (!resource_dir.isDirectory())
492       resource_dir = new File(home_dir, "."+dir_name); // add a dot in front
493     if (!resource_dir.isDirectory())
494       resource_dir = new File(home_dir, dir_name.toLowerCase()); // try it lowercased
495     if (!resource_dir.isDirectory())
496       resource_dir = new File(home_dir, "."+dir_name.toLowerCase()); // try it lowercased and added dot
497     
498     if (!resource_dir.isDirectory())
499       // directory not found! -> use standard way
500       resource_dir = new File(home_dir, dir_name.toLowerCase());
501     return(resource_dir.getPath());
502   }
503 
504   //----------------------------------------------------------------------
505   /**
506    * Returns the user resource file. located in
507    * <code>user_resource_dir</code> with the base name
508    * <code>base_name</code>.
509    *
510    * @param base_name he base name of the users resource file.
511    * @param user_resource_dir the name of the directory within the users
512    * homedir to look for a property file.
513    * @return the resource file found in the users home dir.
514    */
515 
516   protected static File getUsersResourceFile(String base_name, 
517                                              String user_resource_dir)
518   {
519     File resource_dir;
520     if (user_resource_dir == null)
521       resource_dir = new File(System.getProperty("user.home"));
522     else
523       resource_dir = new File(user_resource_dir);
524     return(new File(resource_dir, base_name+PROPERTY_EXTENSION));
525   }
526 
527   //----------------------------------------------------------------------
528   /**
529    * Returns the user resource bundle.
530    *
531    * @param user_resource_file the users resource file
532    * @return the resource bundle found in the users home dir, or null if
533    * no resource file exists.
534    */
535 
536   protected static Properties getUsersResourceBundle(File
537                                                      user_resource_file)
538   {
539     if (!user_resource_file.isFile())
540       return(null);
541 
542     try
543     {
544       Properties properties = new Properties();
545       properties.load(new
546         FileInputStream(user_resource_file));
547       return(properties);
548     }
549     catch (FileNotFoundException exc)
550     {
551           // silently ignored
552     }
553     catch (IOException exc)
554     {
555           // silently ignored
556     }
557     return(null);
558   }
559 
560   //----------------------------------------------------------------------
561   /**
562    * Returns true, if this resources is capable of storing and
563    * deleting resources, false otherwise.
564    *
565    * @return true, if this resources is capable of storing and
566    * deleting resources, false otherwise.
567    */
568 
569   public boolean isModificationSupported()
570   {
571     return(true);
572   }
573 
574   //----------------------------------------------------------------------
575   /**
576    * Gets the bound value for the given key.
577    *
578    * @param key the key of the resource property to look for.
579    * @return the string loaded from the resource bundle.
580    * @throws MissingResourceException if the value is not found.
581    */
582   
583   protected synchronized String getValue(String key)
584     throws MissingResourceException
585   {
586     String value = user_properties_.getProperty(key);
587     if (value != null)
588       return(value);
589     return(system_bundle_.getString(key)); // the system bundle
590   }
591 
592   //----------------------------------------------------------------------
593   /**
594    * Returns an Enumeration containing all keys of all resources.
595    *
596    * @return an Enumeration containing all keys of all resources.
597    */
598 
599   protected Enumeration doGetKeys()
600   {
601     return(new FileResourcesEnumeration());
602   }
603 
604   //----------------------------------------------------------------------
605   /**
606    * Registers the given value under the given key. Key and value are
607    * garanteed to be non-null!
608    * Overwrite this method in classes extending AbstractResources if
609    * set methods are supported.
610    *
611    * @param key the key of the resource property to set.
612    * @param value the value of the resource property to set.
613    * 
614    */
615   
616   protected synchronized void setValue(String key, String value)
617   {
618     //    System.err.println("setValue() key: "+key+", value: "+value);
619     user_properties_.setProperty(key, value);
620   }
621 
622   //----------------------------------------------------------------------
623   /**
624    * Removes the bound value for the given key, if no value was bound
625    * under the given key, this method does nothing. Key is garanteed
626    * to be non-null!
627    * Overwrite this method in classes extending AbstractResources, if
628    * remove is supported.
629    *
630    * @param key the key of the resource to delete.
631    */
632 
633   protected synchronized void unsetValue(String key)
634   {
635 //    System.err.println("unsetValue() key: "+key);
636     try
637     {
638       system_bundle_.getString(key);
639     }
640     catch(Exception e)
641     {
642       user_properties_.remove(key); // it was not in the system bundle
643       return;
644     }
645           // mark it with a special key, so it is unset from now on!
646     user_properties_.put(key,"$"+UNSET_KEY+"$");
647   }
648 
649   //----------------------------------------------------------------------
650   /**
651    * Resets the bound value for the given key to its default value. If
652    * no value was bound under the given key, this method does
653    * nothing. Key is garanteed to be non-null! Removes the value from
654    * the user properties.
655    *
656    * @param key the key of the resource to reset.
657    */
658 
659   protected void resetValue(String key)
660   {
661     user_properties_.remove(key);
662   }
663   
664   //----------------------------------------------------------------------
665   /**
666    * Call this method to make all changes performed by unset and
667    * setter methods persistent.
668    * Overwrite this method in classes extending AbstractResources, if
669    * modifications are supported. 
670    *
671    * @exception IOException in case of an IOError.
672    */
673 
674   protected synchronized void doStore()
675     throws IOException
676   {
677     String tmp_file_name = user_resource_file_.getName()+".tmp";
678     File parent = user_resource_file_.getParentFile();
679     if (!parent.exists())
680       parent.mkdir();
681         
682     FileOutputStream file_out = new FileOutputStream(user_resource_file_);
683     user_properties_.store(file_out, "auto generated file - by FileResources");
684     file_out.close();
685     return;
686   }
687 
688 
689   //----------------------------------------------------------------------
690   /**
691    * Returns the title for the given key. If no title for this key is
692    * available, <code>null</code> is returned.
693    * Overwrite this method in classes extending AbstractResources, if
694    * titles are supported. by default, this method returns null.
695    *
696    * @param key the key to get the title for.
697    * @return the title for the given key.
698    */
699 
700   public String getTitle(String key)
701   {
702     return(getString(key+TITLE_SUFFIX, null));
703   }
704 
705   //----------------------------------------------------------------------
706   /**
707    * Sets the title for the given key.
708    * Deleted the title if title is 'null'.
709    *
710    * @param key the key to set the title for.
711    * @param title the title to set.
712    * @exception IllegalArgumentException if key is 'null'.
713    */
714 
715   public void setTitle(String key, String title)
716   {
717     if (title == null)
718       unsetValue(key+TITLE_SUFFIX);
719     setValue(key+TITLE_SUFFIX, title);
720   }
721 
722   //----------------------------------------------------------------------
723   /**
724    * Returns a string that describes the key and its possible values.
725    * If no description for this key is available, <code>null</code> is
726    * returned. 
727    * Overwrite this method in classes extending AbstractResources, if
728    * descriptions are supported. by default, this method returns null.
729    *
730    * @param key the key to get the description for.
731    * @return the description of the given key and its possible values.
732    */
733 
734   public String getDescription(String key)
735   {
736     return(getString(key+DESCRIPTION_SUFFIX, null));
737   }
738 
739   //----------------------------------------------------------------------
740   /**
741    * Sets the description for the given key.
742    * Deleted the description if description is 'null'.
743    *
744    * @param key the key to set the description for.
745    * @param description the description to set.
746    * @exception IllegalArgumentException if key is 'null'.
747    */
748 
749   public void setDescription(String key, String description)
750   {
751     if (description == null)
752       unsetValue(key+DESCRIPTION_SUFFIX);
753     setValue(key+DESCRIPTION_SUFFIX, description);
754   }
755 
756   //----------------------------------------------------------------------
757   /**
758    * Returns the type of the value bound under the given key. Note
759    * that the returned "Class" object may describe a built-in Java
760    * type such as "int" (Integer.TYPE). For arrays, this may be e.g.:
761    * <code>int[].class</code>. If no type for this key is available,
762    * <code>null</code> is returned.
763    * Overwrite this method in classes extending AbstractResources, if
764    * types are supported. by default, this method returns null.
765    *
766    * @param key the key to get the type for.
767    * @return the type of the value bound under the given key.
768    */
769 
770   public Class getType(String key)
771   {
772     String class_name = getString(key+TYPE_SUFFIX, null);
773     return(getClassForType(class_name));
774   }
775 
776   //----------------------------------------------------------------------
777   /**
778    * Sets the type for the given key.
779    * Deleted the kype if type is 'null'.
780    *
781    * @param key the key to set the type for.
782    * @param type the type to set.
783    * @exception IllegalArgumentException if key is 'null'.
784    */
785 
786   public void setType(String key, Class type)
787   {
788     String class_name = getTypeForClass(type);
789     if (class_name == null)
790       unsetValue(key+TYPE_SUFFIX);
791     else
792       setValue(key+TYPE_SUFFIX, class_name);
793   }
794 
795   //----------------------------------------------------------------------
796   /**
797    * Returns an array of all values that are valid. This is usefull if
798    * a value may be choosen out of a predifined set of possible
799    * values.
800    * If no set of valid values exists <code>null</code> is returned.
801    * Overwrite this method in classes extending AbstractResources, if
802    * types is a set of chooseable values. by default, this method
803    * returns null. 
804    *
805    * @param key the key to get the type for.
806    * @return the possible values.
807    */
808 
809   public String[] getPossibleValues(String key)
810   {
811     return(getStringArray(key+POSSIBLE_VALUES_SUFFIX,
812                           (String[])null)); 
813   }
814 
815   //----------------------------------------------------------------------
816   /**
817    * Sets the possible Values for the given key.
818    * Deleted the possible Values if possible_values are 'null'.
819    *
820    * @param key the key to set the possible Values for.
821    * @param possible_values the possible Values to set.
822    * @exception IllegalArgumentException if is 'null'. 
823    */
824 
825   public void setPossibleValues(String key, String[] possible_values)
826   {
827     if (possible_values == null)
828       unsetValue(key+POSSIBLE_VALUES_SUFFIX);
829     else
830       setStringArray(key+POSSIBLE_VALUES_SUFFIX, possible_values);
831   }
832 
833   //----------------------------------------------------------------------
834   /**
835    * Returns the Url loaded from the resource bundle. The key in the
836    * resource file may be relative to the resource file. Any variables
837    * found within the properties value will be replaced.
838    *
839    * @param key the key of the resource property to look for.
840    * @return the Url that is loaded from the resource bundle.
841    * @exception MissingResourceException if the given key is not defined
842    * within the resources, or is not a valid Url.
843    */
844 
845   public URL getURL(String key)
846     throws MissingResourceException
847   {
848     String value = getString(key);
849     if (value == null)
850       return(null);
851     try
852     {
853       return(new URL(value));
854     }
855     catch (MalformedURLException exc)
856     {
857       // try it from private resource file ...
858       File file = new File(user_resource_base_name_, value);
859       if (file.exists())
860       {
861         try
862         {
863           return(file.toURL());
864         }
865         catch (MalformedURLException exc2)
866         {
867           // ignored
868         }
869       }
870       
871       // try it with class loader
872       URL ret = ClassLoader.getSystemResource(system_resource_base_name_+"/"+value);
873       if (ret == null) // try it without base name
874         ret = ClassLoader.getSystemResource(value);
875 
876       if (ret != null)
877         return(ret);
878       
879       throw(new MissingResourceException("malformed URL '"+value+"'", 
880                                          getClass().getName(), key));
881     }
882   }
883 
884   //----------------------------------------------------------------------
885   /**
886    * The Enumeration for this FileResources.
887    *
888    * @author Dieter Freismuth
889    * @version $Revision: 1.7 $
890    */
891 
892   class FileResourcesEnumeration implements Enumeration
893   {
894     protected Enumeration enum_;
895 
896     //----------------------------------------------------------------------
897     /**
898      */
899 
900     FileResourcesEnumeration()
901     {
902       HashSet added = new HashSet();
903       Vector keys = new Vector();
904       Enumeration enum = system_bundle_.getKeys();
905       String key;
906       while (enum.hasMoreElements())
907       {
908         key = (String)enum.nextElement();
909         if ((!added.contains(key)) &&
910             (!isSpecial(key)))
911         {
912           keys.add(key);
913           added.add(key);
914         }
915       }
916       enum = user_properties_.propertyNames();
917       while (enum.hasMoreElements())
918       {
919         key = (String)enum.nextElement();
920         if ((!added.contains(key)) &&
921             (!isSpecial(key)))
922         {
923           keys.add(key);
924           added.add(key);
925         }
926       }
927       enum_ = keys.elements();
928     }
929 
930     //----------------------------------------------------------------------
931     /**
932      * @param key the key to check for.
933      * @return if the key is an extension to a resource, ending with
934      * .type, .description, .title or .possible_values.
935      */
936 
937     protected boolean isSpecial(String key)
938     {
939       int delimiter = key.lastIndexOf(".");
940       if (delimiter <= 0)
941         return(false);
942       if ((!key.endsWith(TITLE_SUFFIX)) &&
943           (!key.endsWith(DESCRIPTION_SUFFIX)) &&
944           (!key.endsWith(TYPE_SUFFIX)) &&
945           (!key.endsWith(POSSIBLE_VALUES_SUFFIX)))
946         return(false);
947       return(true);
948     }
949 
950     //----------------------------------------------------------------------
951     /**
952      * Tests if this enumeration contains more elements.
953      *
954      * @return  <code>true</code> if and only if this enumeration object
955      *           contains at least one more element to provide;
956      *          <code>false</code> otherwise.
957      */
958 
959     public boolean hasMoreElements()
960     {
961       return(enum_.hasMoreElements());
962     }
963 
964     //----------------------------------------------------------------------
965     /**
966      * Returns the next element of this enumeration if this enumeration
967      * object has at least one more element to provide.
968      *
969      * @return     the next element of this enumeration.
970      * @exception  NoSuchElementException  if no more elements exist.
971      */
972     public Object nextElement()
973     {
974       return(enum_.nextElement());
975     }
976   }
977 }
978 
979 
980 
981 
982 
983 
984 
985 
986 
987 
988