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 java.util.Enumeration;
49  import java.io.PrintStream;
50  import java.text.DateFormat;
51  import org.melati.poem.util.LimitedEnumeration;
52  import org.melati.poem.util.MappedEnumeration;
53  
54  /**
55   * A Field.
56   * A field is a value (the raw) with its metadata 
57   * (a set of attributes) and possibly an access violation 
58   * if the current user is not allowed to access it. 
59   * 
60   */
61  public class Field<T> implements FieldAttributes<T>, Cloneable {
62  
63    private AccessPoemException accessException;
64    private Object raw;
65    private FieldAttributes<T> attrs;
66  
67    /**
68     * Constructor.
69     * 
70     * @param raw the object value, integer for reference types
71     * @param attrs the metadata attributes to set
72     */
73    public Field(Object raw, FieldAttributes<T> attrs) {
74      this.raw = raw;
75      this.attrs = attrs;
76      accessException = null;
77    }      
78  
79    /**
80     * Constructor for a Field with an access violation.
81     * 
82     * @param accessException the access violation
83     * @param attrs the metadata attributes to set
84     */
85    public Field(AccessPoemException accessException, FieldAttributes<T> attrs) {
86      this.accessException = accessException;
87      this.attrs = attrs;
88      raw = null;
89    }
90  
91    // 
92    // -----------
93    //  Cloneable
94    // -----------
95    // 
96  
97    /**
98     * {@inheritDoc}
99     * @see java.lang.Object#clone()
100    */
101   public Object clone() {
102     try {
103       return super.clone();
104     }
105     catch (CloneNotSupportedException e) {
106       throw new UnexpectedExceptionPoemException(e, "Object no longer supports clone.");
107     }
108   }
109 
110   // 
111   // -----------------
112   //  FieldAttributes
113   // -----------------
114   // 
115 
116   /**
117    * {@inheritDoc}
118    * @see org.melati.poem.FieldAttributes#getName()
119    */
120   public String getName() {
121     return attrs.getName();
122   }
123 
124   /**
125    * {@inheritDoc}
126    * @see org.melati.poem.FieldAttributes#getDisplayName()
127    */
128   public String getDisplayName() {
129     return attrs.getDisplayName();
130   }
131 
132   /**
133    * {@inheritDoc}
134    * @see org.melati.poem.FieldAttributes#getDescription()
135    */
136   public String getDescription() {
137     return attrs.getDescription();
138   }
139 
140   /**
141    * {@inheritDoc}
142    * @see org.melati.poem.FieldAttributes#getType()
143    */
144   public PoemType<T> getType() {
145     return attrs.getType();
146   }
147 
148   /**
149    * {@inheritDoc}
150    * @see org.melati.poem.FieldAttributes#getIndexed()
151    */
152   public boolean getIndexed() {
153     return attrs.getIndexed();
154   }
155 
156   /**
157    * {@inheritDoc}
158    * @see org.melati.poem.FieldAttributes#getUserEditable()
159    */
160   public boolean getUserEditable() {
161     return attrs.getUserEditable();
162   }
163 
164   /**
165    * {@inheritDoc}
166    * @see org.melati.poem.FieldAttributes#getUserCreateable()
167    */
168   public boolean getUserCreateable() {
169     return attrs.getUserCreateable();
170   }
171 
172   /**
173    * {@inheritDoc}
174    * @see org.melati.poem.FieldAttributes#getWidth()
175    */
176   public int getWidth() {
177     return attrs.getWidth();
178   }
179 
180   /**
181    * {@inheritDoc}
182    * @see org.melati.poem.FieldAttributes#getHeight()
183    */
184   public int getHeight() {
185     return attrs.getHeight();
186   }
187 
188   /**
189    * {@inheritDoc}
190    * @see org.melati.poem.FieldAttributes#getRenderInfo()
191    */
192   public String getRenderInfo() {
193     return attrs.getRenderInfo();
194   }
195 
196   // 
197   // -------
198   //  Field
199   // -------
200   // 
201 
202   /**
203    * Get the value of the Field.
204    * 
205    * @return the Object value, Integer for reference types
206    * @throws AccessPoemException
207    * FIXME Integer/Persistent issue
208    */
209   @SuppressWarnings("unchecked")
210   public final T getRaw() throws AccessPoemException {
211     if (accessException != null)
212       throw accessException;
213     return (T)raw;
214   }
215 
216   /**
217    * Get the value as a String.
218    * 
219    * @return the String representation of this Field.
220    * @throws AccessPoemException if the current AccessToken does not permit reading
221    */
222   public final Object getRawString() throws AccessPoemException {
223     if (accessException != null)
224       throw accessException;
225     return raw == null ? "" : getType().stringOfRaw(raw);
226   }
227 
228   /**
229    * @return the object represented by the raw
230    * @throws AccessPoemException if the current AccessToken does not permit reading
231    */
232   public final Object getCooked() throws AccessPoemException {
233     if (accessException != null)
234       throw accessException;
235     return getType().cookedOfRaw(raw);
236   }
237 
238   /**
239    * @return cooked value as a String with defaulted Locale and DateFormat
240    * @throws AccessPoemException
241    */
242   public final String getCookedString()
243       throws AccessPoemException {
244     return getCookedString(PoemLocale.HERE,java.text.DateFormat.SHORT); 
245   }
246   /**
247    * @param locale used in date rendering
248    * @param style  used in date rendering
249    * @return a String representation of the Object represented by the raw
250    * @throws AccessPoemException if the current AccessToken does not permit reading
251    */
252   public final String getCookedString(PoemLocale locale, int style)
253   throws AccessPoemException {
254     if (accessException != null)
255       throw accessException;
256     return raw == null ? "" :
257                          getType().stringOfCooked(getCooked(), locale, style);
258   }
259 
260   /**
261    * Clone this Field with a new value but same metadata.
262    * 
263    * @param rawP new value to set
264    * @return a clone with the raw value set to new value
265    */
266   public Field<T> withRaw(T rawP) {
267     @SuppressWarnings("unchecked")
268     Field<T> it = (Field<T>)clone();
269     it.raw = rawP;
270     return it;
271   }
272 
273   /**
274    * Clone with a different nullability.
275    * 
276    * @param nullable the new nullability
277    * @return a new Field with a new, presumably different, nullability
278    */
279   public Field<T> withNullable(boolean nullable) {
280     return new Field<T>(raw, new BaseFieldAttributes<T>(attrs, nullable));
281   }
282 
283   /**
284    * Clone with a new name.
285    * 
286    * @param name the new name
287    * @return a new Field with a new name
288    */
289   public Field<T> withName(String name) {
290     return new Field<T>(raw, new BaseFieldAttributes<T>(attrs, name));
291   }
292 
293   /**
294    * Clone with a new description.
295    * 
296    * @param description the new description
297    * @return a new Field with a new description
298    */
299   public Field<T> withDescription(String description) {
300     return new Field<T>(raw, new BaseFieldAttributes<T>(
301                                         attrs, attrs.getName(), description));
302   }
303 
304   /**
305    * Might be a bit big for some Reference types.
306    * Returns <code>null</code> for String or Integer Types.
307    * 
308    * @return All possible values or null.
309    */
310   public Enumeration<Field<T>> getPossibilities() {
311     final Field<T> _this = this;
312     Enumeration<T> en = getType().possibleRaws();
313     return
314         en == null ? null :
315           new MappedEnumeration<Field<T>,T>(en) {
316             protected Field<T> mapped(T rawP) {
317               return _this.withRaw(rawP);
318             }
319           };
320   }
321 
322   /**
323    * Return a limited enumeration of possibilities.
324    * 
325    * A bit of a hack?
326    * @return the first 100 possibilities or null
327    */
328   public Enumeration<Field<T>> getFirst1000Possibilities() {
329     Enumeration<Field<T>> en = getPossibilities();
330     return en == null ? null : new LimitedEnumeration<Field<T>>(en, 1000);
331   }
332 
333   /**
334    * Compare raws.
335    * 
336    * @param other another field to check
337    * @return whether the other field has the same raw value as this one
338    * @throws AccessPoemException if it is already set
339    */
340   public boolean sameRawAs(Field<T> other) throws AccessPoemException {
341     if (accessException != null)
342       throw accessException;
343     return raw == null ? other.raw == null : raw.equals(other.raw);
344   }
345 
346   /**
347    * Dump to a PrintStream.
348    * 
349    * @param p the PRintStream to write to
350    */
351   public void dump(PrintStream p) {
352     p.print(toString());
353   }
354   
355   /**
356    * Dump to a string.
357    * 
358    * {@inheritDoc}
359    * @see java.lang.Object#toString()
360    */
361   public String toString() {
362     return getName() + ": " + getCookedString(PoemLocale.HERE,
363                                                DateFormat.MEDIUM);
364   }
365 
366   /**
367    * A convenience method to create a Field.
368    * 
369    * @param value the Object to set the value to 
370    * @param name the name of the new Field, also used as description
371    * @param type the PoemType of the Field
372    * @return a newly created Field
373    */
374   @SuppressWarnings({ "unchecked", "rawtypes" })
375   public static Field basic(Object value, String name, PoemType type) {
376     return
377         new Field(value,
378                   new BaseFieldAttributes(name, name, null, type, 20, 1, null,
379                                           false, true, true));
380   }
381 
382   /**
383    * A convenience method to create nullable String Field.
384    * 
385    * @param value the String to set the value to 
386    * @param name the name of the new Field, also used as description
387    * @return a newly created nullable Field of type StringPoemType
388    */
389   @SuppressWarnings("rawtypes")
390   public static Field string(String value, String name) {
391     return basic(value, name, StringPoemType.nullableInstance);
392   }
393 
394   /**
395    * A convenience method to create nullable Integer Field.
396    * 
397    * @param value the Integer to set the value to 
398    * @param name the name of the new Field, also used as description
399    * @return a newly created nullable Field of type IntegerPoemType
400    */
401   @SuppressWarnings("rawtypes")
402   public static Field integer(Integer value, String name) {
403     return basic(value, name, IntegerPoemType.nullableInstance);
404   }
405 
406   /**
407    * A convenience method to create a populated, nullable, Reference Field.
408    * 
409    * @param value the Persistent to set the value to 
410    * @param name the name of the new Field, also used as description
411    * @return a newly created nullable Field of type ReferencePoemType
412    */
413   @SuppressWarnings("rawtypes")
414   public static Field reference(Persistent value, String name) {
415     return basic(value.troid(), name,
416                  new ReferencePoemType(value.getTable(), true));
417   }
418 
419   /**
420    * A convenience method to create new unpopulated, nullable Reference Field.
421    * 
422    * @param table the Table to refer to  
423    * @param name the name of the new Field, also used as description
424    * @return a newly created nullable Field of type ReferencePoemType
425    */
426   @SuppressWarnings("rawtypes")
427   public static Field reference(Table table, String name) {
428     return basic(null, name, new ReferencePoemType(table, true));
429   }
430 }