View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc@paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  package org.melati.poem;
46  
47  import org.melati.poem.generated.SettingTableBase;
48  import java.util.Hashtable;
49  
50  /**
51   * A {@link Table} which is used like a properties file.
52   *
53   * Every Melati DB has one.
54   *
55   * Extended from a Melati POEM generated stub. 
56   * 
57   * <p>
58   * Description: 
59   *   A configurable setting for the application. 
60   * </p>
61   *
62   * 
63   * <table> 
64   * <caption>
65   * Field summary for SQL table <code>Setting</code>
66   * </caption>
67   * <tr><th>Name</th><th>Type</th><th>Description</th></tr>
68   * <tr><td> id </td><td> Integer </td><td> The Table Row Object ID </td></tr> 
69   * <tr><td> name </td><td> String </td><td> A code name for this setting 
70   * </td></tr> 
71   * <tr><td> value </td><td> String </td><td> The value of this setting 
72   * </td></tr> 
73   * </table> 
74   * 
75   * See  org.melati.poem.prepro.TableDef#generateTableMainJava 
76   */
77  
78  public class SettingTable<T extends Setting> extends SettingTableBase<Setting> {
79  
80   /**
81    * Constructor.
82    * 
83    * See org.melati.poem.prepro.TableDef#generateTableMainJava 
84    * @param database          the POEM database we are using
85    * @param name              the name of this <code>Table</code>
86    * @param definitionSource  which definition is being used
87    * @throws PoemException    if anything goes wrong
88    */
89    public SettingTable(
90        Database database, String name,
91        DefinitionSource definitionSource) throws PoemException {
92      super(database, name, definitionSource);
93    }
94  
95    // programmer's domain-specific code here
96  
97    private static final Object nullEntry = new Object();
98    private Hashtable<String, Object> cache = null;
99    private long cacheSerial = 0L;
100 
101   /**
102    * Get an object of the appropriate type by name.
103    * 
104    * Note this has not worked prior to 25/10/2006, 
105    * a new cache was created every time.
106    * Transactions are so fine grained that it is not clear that 
107    * this cache serves any purpose. 
108    * 
109    * @param name the name field of this Setting object
110    * @return its value, cast to the appropriate type.
111    */
112   public Object getCooked(String name) {
113     if (cache == null || cacheSerial != serial(PoemThread.transaction())) {
114       cacheSerial = serial(PoemThread.transaction());
115       cache = new Hashtable<String, Object>();
116     } else {
117       Object value = cache.get(name);
118       if (value == nullEntry)
119         return null;
120       else if (value != null) 
121         return value;
122     } 
123     Setting prop =
124           (Setting)getNameColumn().firstWhereEq(name);
125     if (prop == null) {
126       cache.put(name, nullEntry);
127       return null;
128     }
129     else {
130       Object propValue = prop.getCooked();
131       cache.put(name, propValue == null ? nullEntry : propValue);
132       return propValue;
133     }
134   }
135 
136   /**
137    * Get the String representation of the setting.
138    * @param name the name field of the Setting object
139    * @return the value as a String or null
140    */
141   public String get(String name) {
142     Object it = getCooked(name);
143     return it == null ? null : it.toString();
144   }
145 
146  /**
147   * Thrown when a {@link Setting} requested has not been set. 
148   */
149   public static class UnsetException extends PoemException {
150     private static final long serialVersionUID = 1L;
151 
152     /** The name of the requested setting. */
153     public String name;
154 
155     /** Constructor. */
156     public UnsetException(String name) {
157       this.name = name;
158     }
159 
160     /** @return The detail message. */
161     public String getMessage() {
162       return "The application's `" + name + "' parameter has not been set";
163     }
164   }
165 
166   /**
167    * Get a set value.
168    * 
169    * @param name the Setting's name field
170    * @return the Setting's value as an appropriate Object
171    */
172   public Object getOrDie(String name) {
173     Object it = get(name);
174     if (it == null)
175       throw new UnsetException(name);
176     return it;
177   }
178 
179   /**
180    * Make sure that a setting with this name exists, if not then create it.
181    * Note that only the name is checked for existence, the other parameters 
182    * are only used for creation, so enabling the setting to be manually changed. 
183    * 
184    * @param name the name of the Setting to ensure
185    * @param typefactory what type to use
186    * @param value the value of this setting
187    * @param displayname a human readable name 
188    * @param description the purpose of the setting
189    * @return the existing or newly created Setting
190    */
191   public Setting ensure(String name, PoemTypeFactory typefactory, Object value,
192                         String displayname, String description) {
193     Setting setting = (Setting)getNameColumn().firstWhereEq(name);
194     if (setting != null)
195       return setting;
196     else {
197       setting = (Setting)newPersistent();
198       setting.setName(name);
199       setting.setDisplayname(displayname);
200       setting.setDescription(description);
201 
202       setting.setUsereditable(true);
203 
204       setting.setWidth(20);
205       setting.setHeight(1);
206       setting.setPrecision(22);
207       setting.setScale(2);
208 
209       setting.setNullable(true);
210       setting.setSize(-1);
211       setting.setTypefactory(typefactory);
212 
213       setting.setRaw(value);
214 
215       return (Setting)getNameColumn().ensure(setting);
216     }
217   }
218 
219   /**
220    * Convenience method. 
221    * 
222    * @param name the name of the Setting to ensure
223    * @param value the value of this setting
224    * @param displayname a human readable name 
225    * @param description the purpose of the setting
226    * @return the existing or newly created Setting
227    * @see #ensure(String, PoemTypeFactory, Object, String, String)
228    */
229   public Setting ensure(String name, String value,
230                         String displayname, String description) {
231     return ensure(name, PoemTypeFactory.STRING, value,
232                   displayname, description);
233   }
234 
235   /**
236    * Convenience method. 
237    * @param name the name of the Setting to ensure
238    * @param value the value of this setting
239    * @param displayname a human readable name 
240    * @param description the purpose of the setting
241    * @return the existing or newly created Setting
242    * @see #ensure(String, PoemTypeFactory, Object, String, String)
243    */
244   public Setting ensure(String name, int value,
245                         String displayname, String description) {
246     return ensure(name, PoemTypeFactory.INTEGER, new Integer(value),
247                   displayname, description);
248   }
249   
250   /**
251    * Convenience method. 
252    * @param name the name of the Setting to ensure
253    * @param value the value of this setting
254    * @param displayname a human readable name 
255    * @param description the purpose of the setting
256    * @return the existing or newly created Setting
257    * @see #ensure(String, PoemTypeFactory, Object, String, String)
258    */
259   public Setting ensure(String name, boolean value, 
260                            String displayname, String description) { 
261        return ensure(name, PoemTypeFactory.BOOLEAN, 
262                      value ? Boolean.TRUE : Boolean.FALSE, 
263                      displayname, description); 
264      } 
265 }