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 }