001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    import java.io.*;
009    import fc.io.*;
010    
011    /** 
012    Implementations help load and manage properties from various
013    sources including java property files, databases, custom
014    file formats etc. Regardless of where the properties are
015    loaded from, each property consists of a pair of <b>name</b>
016    and <b>value</b> strings.
017    
018    @author hursh jain 
019    @version 1.0 12/30/2001
020    */
021    public abstract class PropertyMgr
022    {
023    protected String  usage;
024    protected Log     log;
025    
026    /**
027    Constructs a new PropertyMgr. 
028    **/
029    public PropertyMgr() {
030      this.log = Log.getDefault();  
031      }
032    
033    /**
034    Returns the property associated with the specified key or <tt>null</tt>
035    if the property was not found.
036    @param  name  the property key
037    **/
038    public abstract String get(String name);
039    
040    
041    /**
042    Returns the property associated with the specified key.
043    @param  name  the property key
044    @param  backup  value to return if the property for the specified key is not found  
045    
046    @return value of specified key or backup string
047    **/
048    public String get(String name, String backup)
049      {
050      String val = get(name);
051      if (val == null) 
052        {
053        return backup;  
054        }
055      else return val;
056      }
057    
058    /**
059    Returns the property associated with the specified key as a
060    boolean value. If the property <b>is</b> present, then the
061    corresponding boolean value is <tt>true</tt> if the property
062    value is any of the following (case-insensitive):
063    <blockquote>
064    <code>
065    yes, 1, true
066    </code>
067    <blockquote>
068    else <tt>false</tt> is returned.
069    
070    @param  name  the property key
071    @param  backup  value to return if the property for the 
072            specified property is <b>not</b> present  
073    
074    @return value of specified key or backup string
075    */
076    public boolean getBoolean(String name, boolean backup) 
077      {
078        String val = get(name);
079        if (val == null)
080           return backup;
081        else
082           return toBoolean(val);
083        }
084    
085    /**
086    Returns the property associated with the specified key as a
087    integer value. If the property is present but cannot be
088    converted into an integer (via a {@link
089    Integer.parseInt(String)} call), the backup value will be
090    returned.
091    
092    @param  name  the property key
093    @param  backup  value to return if the property for the 
094            specified property is <b>not</b> present  
095    
096    @return value of specified key or backup string
097    */
098    public int getInt(String name, int backup) 
099      {
100        String val = get(name);
101        if (val != null)
102          {
103          try {
104            return Integer.parseInt(val);
105            }
106          catch (NumberFormatException e) {
107            log.warn("Cannot convert property '" + name + "', into a number, returning backup value " + backup);
108            }
109          }
110      return backup;
111        }
112    
113    /** 
114    Returns the value corresponding to the specified key. If the
115    property <b>value</b> is not found, the {@link #handleError} method
116    is called, which by default prints a stack trace and exits
117    the application.
118    <p>
119    If the handleError method is overriden to <b>not</b> exit
120    the application, then this method will return <tt>null</tt>
121    if the specified key is not found.
122    
123    @param  name  the property key
124    **/
125    public String getRequired(String name)
126      {
127      String val = get(name);
128      if (val == null || val.trim().length() == 0) {
129        try { 
130          handleError(name);
131          }
132        catch (Exception e) { 
133          /* 
134          If the handler throws us an Exception, we pass
135          it along as a Runtime Exception (since this
136          method does not throw a checked exception). Of
137          course, handlers are free to deal with
138          Exceptions in any other way they see fit.
139          */
140          throw new RuntimeException(e); 
141          }
142        }
143      return val;
144      }
145    
146    /*
147    Returns the value obtained via {@link getRequired(String)}
148    as as a boolean. The boolean value returned is
149    <code>true</code> if the property value is any of the
150    following (case-insensitive):
151    <blockquote>
152    <code>
153    yes, 1, true
154    </code>
155    <blockquote>
156    else <tt>false</tt> is returned.
157    */
158    public boolean getRequiredBoolean(String name) 
159      {
160      String val = getRequired(name);
161      return toBoolean(val);  
162      }
163    
164    /*
165    Returns the value obtained via {@link getRequired(String)} as
166    as an integer. If the property cannot be converted into an integer (via a {@link
167    Integer.parseInt(String)} call), the resulting action would
168    be the same as if the required key was not present.
169    */
170    public int getRequiredInt(String name)
171      {
172      String val = getRequired(name);
173        
174        int result = 0;
175        
176        try {
177          result = Integer.parseInt(val);
178          }
179      catch (NumberFormatException ne) {
180          log.warn("Cannot convert property '" + name + "', into a number");
181        try { 
182          handleError(name);
183          }
184        catch (Exception e) { 
185          /* 
186          If the handler throws us an Exception, we pass
187          it along as a Runtime Exception (since this
188          method does not throw a checked exception). Of
189          course, handlers are free to deal with
190          Exceptions in any other way they see fit.
191          */
192          throw new RuntimeException(e); 
193          }
194        }
195      
196      return result;
197      }
198      
199    /**
200    Sets the property associated with the specified key.
201    @param  name  the property key
202    @param  backup  the property value  
203    @return the previous value of the specified key or null if it did not 
204        have one.
205    **/
206    public abstract String set(String name, String backup);
207    
208    /** 
209    Saves any properties that were set previously. This method does not
210    need to be called if properties were only read, however if any
211    property was modified or added, it is essential to call this method
212    to save any such changes.
213    **/
214    public abstract void save() throws IOException;
215    
216    /** 
217    Specify program usage information to be output when an error occurs. This 
218    information should contain a short description of required keys and any
219    other information that the application expects.
220    @param str  Usage information
221    **/
222    public void setUsage(String str)
223      {
224      this.usage = str;   
225      }
226    
227    /** 
228    Default implementation:<br>
229    Handles all error situation - such as when a required property is
230    not found. By default, prints an error message to <tt>System.err</tt> and 
231    exits the JVM by calling <tt>System.exit(1)</tt>. 
232    This method can be overriden in subclasses to handle errors differently.
233    @param    str     the error message
234    @throws   Exception sub classes can throw an Exception, if necessary
235    **/
236    protected void handleError(String str) throws PropertyNotFoundException
237      {
238      log.error("Application is missing required Property: " + str);
239      new Exception().printStackTrace(System.err);
240      if (usage != null) {
241        log.error(usage);
242        }
243      System.exit(1);
244      }
245    
246    final boolean toBoolean(String val)
247      {
248      val = val.trim().toLowerCase().intern();
249      if ( val == "true" || val == "yes" || val == "1" )
250        return true;
251      return false;
252      }
253    
254    } //~class PropertyMgr