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 At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem;
47  
48  import org.melati.poem.generated.SettingBase;
49  
50  /**
51   * A setting, analageous to a Property.
52   * <p>
53   * NOTE While the underlying value is held as a String 
54   * that is converted to any type.
55   * 
56   * <p>
57   * 
58   * Melati POEM generated, modified definition of  
59   * a <code>Persistent</code> <code>Setting</code> object.
60   * 
61   * <p> 
62   * Description: 
63   *   A configurable setting for the application. 
64   * </p>
65   * 
66   * <table> 
67   * <caption>
68   * Field summary for SQL table <code>Setting</code>
69   * </caption>
70   * <tr><th>Name</th><th>Type</th><th>Description</th></tr>
71   * <tr><td> id </td><td> Integer </td><td> The Table Row Object ID </td></tr> 
72   * <tr><td> name </td><td> String </td><td> A code name for this setting 
73   * </td></tr> 
74   * <tr><td> value </td><td> String </td><td> The value of this setting 
75   * </td></tr> 
76   * </table> 
77   * 
78   * See org.melati.poem.prepro.TableDef#generateMainJava 
79   * @author WilliamC@paneris.org
80   */
81  
82  public class Setting extends SettingBase {
83   /**
84    * Constructor 
85    * for a <code>Persistent</code> <code>Setting</code> object.
86    * <p>
87    * Description: 
88    *   A configurable setting for the application. 
89    * </p>
90    * 
91    * See org.melati.poem.prepro.TableDef#generateMainJava 
92    */
93    public Setting() { }
94  
95    // programmer's domain-specific code here
96  
97    private FieldAttributes<?> valueAttributes = null;
98    private Object raw = null;
99    private Object cooked = null;
100 
101   /**
102    * Constructor with reasonable defaults for a String setting
103    * Use SettingTable.create to persist this.
104    * @param typefactory The type factory integer code
105    * @param name the name of the setting
106    * @param value the string value
107    * @param displayname the name to display
108    * @param description the description to display
109    */
110   public Setting(Integer typefactory, String name, String value,
111                  String displayname, String description) {
112     setTypefactory_unsafe(typefactory);
113     setName_unsafe(name);
114     setValue_unsafe(value);
115     setDisplayname_unsafe(displayname);
116     setDescription_unsafe(description);
117     setUsereditable_unsafe(Boolean.TRUE);
118     setNullable_unsafe(Boolean.TRUE);
119     setSize_unsafe(new Integer(-1));
120     setWidth_unsafe(new Integer(20));
121     setHeight_unsafe(new Integer(1));
122     setPrecision_unsafe(new Integer(22));
123     setScale_unsafe(new Integer(2));
124   }
125 
126  /**
127   * Thrown when a {@link Setting} value fails validation.
128   */
129   public static class SettingValidationException extends PoemException {
130     private static final long serialVersionUID = 1L;
131 
132     /** The name of the requested setting. */
133     public String name;
134 
135     /** Constructor. */
136     public SettingValidationException(String name, Exception problem) {
137       super(problem);
138       this.name = name;
139     }
140 
141     /** @return The detail message. */
142     public String getMessage() {
143       return "A problem arose updating the value of the `" + name +
144       "' setting:\n" + subException.getMessage();
145     }
146   }
147 
148   /**
149    * Check that value is of correct type before setting. 
150    * {@inheritDoc}
151    * @see org.melati.poem.generated.SettingBase#setValue(java.lang.String)
152    */
153   public void setValue(String value) {
154     Object rawLocal;
155     try {
156       rawLocal = getType().rawOfString(value);
157     } catch (Exception e) {
158       throw new SettingValidationException(getName_unsafe(), e);
159     }
160 
161     super.setValue(value);
162     this.raw = rawLocal;
163     cooked = null;
164   }
165 
166   /**
167    * Set from a raw value; checking that the value is of the correct type first.
168    * @param raw the raw to set
169    */
170   public void setRaw(Object raw) {
171     String string;
172     Object newRaw;
173     try {
174       string = getType().stringOfRaw(raw);
175     } catch (Exception e) {
176       throw new SettingValidationException(getName_unsafe(), e);
177     }
178     try {
179       newRaw = getType().rawOfString(string);
180     } catch (Exception e) {
181       throw new SettingValidationException(getName_unsafe(), e);
182     }
183     super.setValue(newRaw == null ? null : string);
184     this.raw = newRaw;
185     cooked = null;
186   }
187 
188   /**
189    * @return the raw value
190    */
191   public Object getRaw() {
192     if (raw == null)
193       try {
194         raw = getType().rawOfString(getValue());
195       } catch (Exception e) {
196         throw new SettingValidationException(getName_unsafe(), e);
197       }
198 
199     return raw;
200   }
201 
202   /**
203    * @return the cooked, ie typed, Object
204    */
205   public Object getCooked() {
206     if (cooked == null)
207       cooked = getType().cookedOfRaw(getRaw());
208     return cooked;
209   }
210 
211  /**
212   * Thrown when a {@link Setting}'s type does not match the type required. 
213   */
214   public static class 
215       SettingTypeMismatchException extends AppBugPoemException {
216     private static final long serialVersionUID = 1L;
217 
218     /** Name of the setting. */
219     public String name;
220     /** The factory used. */
221     public PoemTypeFactory type;
222     /** Required type. */
223     public String reqType;
224 
225     /** Constructor. */
226     public SettingTypeMismatchException(String name, PoemTypeFactory type,
227                                         String reqType) {
228       this.name = name;
229       this.type = type;
230       this.reqType = reqType;
231     }
232 
233     /** @return The detail message. */
234     public String getMessage() {
235       return "The setting `" + name + "' has type `" + type + "' but " +
236       "the application asked for a value of type " + reqType;
237     }
238   }
239 
240   /**
241    * @return value as an Integer
242    */
243   public Integer getIntegerCooked() {
244     Object cookedLocal = getCooked();
245     if (cookedLocal == null && getNullable().booleanValue())
246       return null;
247     else if (cookedLocal instanceof Integer)
248       return (Integer)cookedLocal;
249     else
250       throw new SettingTypeMismatchException(getName_unsafe(),
251                                              getTypefactory(), "Integer");
252   }
253 
254   /**
255    * @return value as a String
256    */
257   public String getStringCooked() {
258     Object cookedLocal = getCooked();
259     if (cookedLocal == null && getNullable().booleanValue())
260       return null;
261     else if (cookedLocal instanceof String)
262       return (String)cookedLocal;
263     else
264       throw new SettingTypeMismatchException(getName_unsafe(),
265                                              getTypefactory(), "String");
266   }
267 
268   /**
269    * @return value as a Boolean
270    */
271   public Boolean getBooleanCooked() {
272     Object cookedLocal = getCooked();
273     if (cookedLocal == null && getNullable().booleanValue())
274       return null;
275     else if (cookedLocal instanceof Boolean)
276       return (Boolean)cookedLocal;
277     else
278       throw new SettingTypeMismatchException(getName_unsafe(),
279                                              getTypefactory(), "Boolean");
280   }
281 
282   /**
283    * @return the attributes set in this Setting as the attributes for the 
284    * value field
285    */
286   @SuppressWarnings("rawtypes")
287   private FieldAttributes valueFieldAttributes() {
288     if (valueAttributes == null)
289       valueAttributes =
290           fieldAttributesRenamedAs(getSettingTable().getValueColumn());
291 
292     return valueAttributes;
293   }
294 
295   /**
296    * Override the normal field attributes for the Value field, 
297    * use the attribute values set in this setting.
298    * @see org.melati.poem.generated.SettingBase#getValueField()
299    */
300   @SuppressWarnings({ "rawtypes", "unchecked" })
301   public Field getValueField() {
302     try {
303       return new Field<String>(getRaw(), valueFieldAttributes());
304     } catch (AccessPoemException accessException) {
305       return new Field<Exception>(accessException, valueFieldAttributes());
306     }
307   }
308 
309   /**
310    * Slight overkill, force recreation of value field attributes even 
311    * if it is the value that has been changed.
312    *   
313    * {@inheritDoc}
314    * @see org.melati.poem.ValueInfo#postEdit(boolean)
315    */
316   public void postEdit(boolean creating) {
317     super.postEdit(creating);
318     valueAttributes  = null;
319   }
320   
321 }