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
006package fc.util;
007
008import java.io.*;
009import fc.io.*;
010
011/** 
012Implementations help load and manage properties from various
013sources including java property files, databases, custom
014file formats etc. Regardless of where the properties are
015loaded from, each property consists of a pair of <b>name</b>
016and <b>value</b> strings.
017
018@author hursh jain 
019@version 1.0 12/30/2001
020*/
021public abstract class PropertyMgr
022{
023protected String  usage;
024protected Log     log;
025
026/**
027Constructs a new PropertyMgr. 
028**/
029public PropertyMgr() {
030  this.log = Log.getDefault();  
031  }
032
033/**
034Returns the property associated with the specified key or <tt>null</tt>
035if the property was not found.
036@param  name  the property key
037**/
038public abstract String get(String name);
039
040
041/**
042Returns 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**/
048public 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/**
059Returns the property associated with the specified key as a
060boolean value. If the property <b>is</b> present, then the
061corresponding boolean value is <tt>true</tt> if the property
062value is any of the following (case-insensitive):
063<blockquote>
064<code>
065yes, 1, true
066</code>
067<blockquote>
068else <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*/
076public 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/**
086Returns the property associated with the specified key as a
087integer value. If the property is present but cannot be
088converted into an integer (via a {@link
089Integer.parseInt(String)} call), the backup value will be
090returned.
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*/
098public 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/** 
114Returns the value corresponding to the specified key. If the
115property <b>value</b> is not found, the {@link #handleError} method
116is called, which by default prints a stack trace and exits
117the application.
118<p>
119If the handleError method is overriden to <b>not</b> exit
120the application, then this method will return <tt>null</tt>
121if the specified key is not found.
122
123@param  name  the property key
124**/
125public 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/*
147Returns the value obtained via {@link getRequired(String)}
148as as a boolean. The boolean value returned is
149<code>true</code> if the property value is any of the
150following (case-insensitive):
151<blockquote>
152<code>
153yes, 1, true
154</code>
155<blockquote>
156else <tt>false</tt> is returned.
157*/
158public boolean getRequiredBoolean(String name) 
159  {
160  String val = getRequired(name);
161  return toBoolean(val);  
162  }
163
164/*
165Returns the value obtained via {@link getRequired(String)} as
166as an integer. If the property cannot be converted into an integer (via a {@link
167Integer.parseInt(String)} call), the resulting action would
168be the same as if the required key was not present.
169*/
170public 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/**
200Sets 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**/
206public abstract String set(String name, String backup);
207
208/** 
209Saves any properties that were set previously. This method does not
210need to be called if properties were only read, however if any
211property was modified or added, it is essential to call this method
212to save any such changes.
213**/
214public abstract void save() throws IOException;
215
216/** 
217Specify program usage information to be output when an error occurs. This 
218information should contain a short description of required keys and any
219other information that the application expects.
220@param str  Usage information
221**/
222public void setUsage(String str)
223  {
224  this.usage = str;   
225  }
226
227/** 
228Default implementation:<br>
229Handles all error situation - such as when a required property is
230not found. By default, prints an error message to <tt>System.err</tt> and 
231exits the JVM by calling <tt>System.exit(1)</tt>. 
232This 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**/
236protected 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
246final 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