1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package nl.openedge.baritus;
32
33 import java.io.ByteArrayOutputStream;
34 import java.io.PrintWriter;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.Locale;
39 import java.util.Map;
40 import java.util.Set;
41
42 import nl.openedge.baritus.converters.Converter;
43 import nl.openedge.baritus.converters.Formatter;
44 import nl.openedge.baritus.util.ValueUtils;
45 import ognl.Ognl;
46
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49
50 /***
51 * FormBeanContext wraps the form bean, errors the current locale and overrideFields.
52 * Furthermore, it acts as a decorator for a HashMap where you can optionally store
53 * attributes you do not want to include as properties in your form bean.
54 *
55 * @author Eelco Hillenius
56 * @author Maurice Marrink
57 */
58 public final class FormBeanContext implements Map
59 {
60
61
62 private Locale currentLocale = null;
63
64
65
66
67
68 private Object bean = null;
69
70
71 private Map errors = null;
72
73
74 private Map overrideFields = null;
75
76
77 private FormBeanCtrlBase controller = null;
78
79
80
81
82
83 private Map attributes = null;
84
85
86 private static Log log = LogFactory.getLog(FormBeanCtrlBase.class);
87
88
89 private static Log formattingLog = LogFactory.getLog(LogConstants.FORMATTING_LOG);
90
91 /*** error key for stacktrace if any */
92 public final static String ERROR_KEY_STACKTRACE = "stacktrace";
93
94 /*** error key for stacktrace if any */
95 public final static String ERROR_KEY_EXCEPTION = "exception";
96
97
98
99 /***
100 * Get the current locale.
101 * @return Locale current locale
102 */
103 public Locale getCurrentLocale()
104 {
105 return currentLocale;
106 }
107
108 /***
109 * Set the current locale.
110 * @param locale current locale
111 */
112 public void setCurrentLocale(Locale locale)
113 {
114 currentLocale = locale;
115 }
116
117 /***
118 * Get the form bean. The current control provided a bean to populate with method
119 * makeFormBean. That bean is saved (before population) in the formBeanContext.
120 *
121 * @return Object the bean that will be populated, and that is returned by makeFormbean
122 */
123 public Object getBean()
124 {
125 return bean;
126 }
127
128 /***
129 * Set the form bean. This is the bean that will be populated,
130 * and that is returned by make formbean.
131 *
132 * @param bean the bean that will be populated, and that is returned by makeFormbean
133 */
134 public void setBean(Object bean)
135 {
136 this.bean = bean;
137 }
138
139
140
141 /***
142 * Get the map with errors. Returns null if no errors are registered.
143 * @return Map map with errors or null if no errors are registered.
144 */
145 public Map getErrors()
146 {
147 return errors;
148 }
149
150 /***
151 * Get the registered error for one field. Returns null if no error is registered
152 * for the given field name.
153 * @param field name of the field to lookup the error for.
154 * @return String the error that was registered for the provided field name, or null
155 * if no error was registered for the provided field name.
156 */
157 public String getError(String field)
158 {
159 return (errors != null) ? (String)errors.get(field) : null;
160 }
161
162 /***
163 * Set the map of errors.
164 * @param errors The map of errors to set
165 */
166 public void setErrors(Map errors)
167 {
168 this.errors = errors;
169 }
170
171 /***
172 * Either add this exception with the given key, or add the stacktrace
173 * of this exception with the given key. Any value that was registered with
174 * the same key prior to this is overwritten.
175 * @param key key to store error under
176 * @param t exception
177 * @param asStackTrace if true, the stacktrace is added; otherwise the exception
178 * is added
179 */
180 public void setError(String key, Throwable t, boolean asStackTrace)
181 {
182 String value = null;
183 if (asStackTrace)
184 {
185 value = getErrorMessage(t);
186 }
187 else
188 {
189 value = t.getMessage();
190 }
191 setError(key, value);
192 }
193
194 /***
195 * Add exception and its stacktrace. Any value that was registered with
196 * the same key prior to this is overwritten.
197 * @param exceptionKey key to use for exception
198 * @param stackTraceKey key to use for stacktrace
199 * @param t exception
200 */
201 public void setError(
202 String exceptionKey,
203 String stackTraceKey,
204 Throwable t)
205 {
206 String stackTrace = getErrorMessage(t);
207 String errorMessage = t.getMessage();
208 setError(stackTraceKey, stackTrace);
209 setError(exceptionKey, errorMessage);
210 }
211
212 /***
213 * Adds an exception with key 'exception' and adds the stacktrace
214 * of this exception with key 'stacktrace'. Any value that was registered with
215 * the same keys prior to this are overwritten.
216 * @param t exception
217 */
218 public void setError(Throwable t)
219 {
220 setError(ERROR_KEY_EXCEPTION, ERROR_KEY_STACKTRACE, t);
221 }
222
223 /***
224 * Adds an exception with key 'exception' and adds either the stacktrace
225 * of this exception with key 'stacktrace' if asStackTrace is true, or add the
226 * exception message with key 'exception' if asStackTrace is false.
227 * Any value that was registered with the same keys prior to this are overwritten.
228 * @param t exception
229 * @param asStackTrace if true, the stacktrace is added; otherwise the exception
230 */
231 public void setError(Throwable t, boolean asStackTrace)
232 {
233 setError(ERROR_KEY_EXCEPTION, t, asStackTrace);
234 }
235
236 /***
237 * Register (or overwrite) an error with the provided key and value.
238 * @param key the key that the error should be registered with.
239 * @param value the value (message) of the error.
240 */
241 public void setError(String key, String value)
242 {
243 if (errors == null) errors = new HashMap();
244
245 errors.put(key, value);
246 }
247
248 /***
249 * De-register (remove) an error that was registered with the provided key.
250 * @param key the key that the error was registered with.
251 */
252 public void removeError(String key)
253 {
254 if (errors != null)
255 {
256 errors.remove(key);
257 }
258 }
259
260
261
262
263 private String getErrorMessage(Throwable t)
264 {
265 String msg;
266 try
267 {
268 ByteArrayOutputStream bos = new ByteArrayOutputStream();
269 PrintWriter pw = new PrintWriter(bos);
270 t.printStackTrace(pw);
271 pw.flush();
272 pw.close();
273 bos.flush();
274 bos.close();
275 msg = bos.toString();
276 }
277 catch (Exception e)
278 {
279 msg = t.getMessage();
280 }
281 return msg;
282 }
283
284 /***
285 * Get the map of failed field values.
286 * @return Map map with override fields
287 */
288 public Map getOverrideFields()
289 {
290 return overrideFields;
291 }
292
293 /***
294 * Set value of field that overrides. WILL overwrite any allready registered override
295 * @param name name of the field/ property
296 * @param value usually the original input value
297 */
298 public void setOverrideField(String name, Object value)
299 {
300 if (overrideFields == null)
301 {
302 overrideFields = new HashMap();
303 }
304 overrideFields.put(name, value);
305 }
306
307 /***
308 * Get the value of the field that was overridden.
309 * E.g. we got in a formbean property 'myDate' of type date.
310 * If form submit gives 'blah', this cannot be parsed as a date.
311 * Now, if setFailedField('myDate', 'blah') is called,
312 * the view can show the 'wrong' value transparently, as an
313 * Velocity EventCardridge will override any property with a
314 * failed field if set.
315 * @param name name of the field/ property
316 * @return Object usually the original input value
317 */
318 public Object getOverrideField(String name)
319 {
320 return (overrideFields != null) ? overrideFields.get(name) : null;
321 }
322
323 /***
324 * Set values of fields that have overrides.
325 * This WILL NOT overwrite allready registered overrides.
326 * @param fields map of fields to override the current values.
327 */
328 public void setOverrideField(Map fields)
329 {
330 if (fields != null)
331 {
332 if (overrideFields == null)
333 {
334 overrideFields = new HashMap();
335 overrideFields.putAll(fields);
336 }
337 else
338 {
339 for (Iterator i = fields.keySet().iterator(); i.hasNext();)
340 {
341 String key = (String)i.next();
342 if (!overrideFields.containsKey(key))
343 {
344 overrideFields.put(key, fields.get(key));
345 }
346 }
347 }
348 }
349 }
350
351 /***
352 * Whether any errors were registered during population/ validation.
353 * @return boolean are there any errors registered for this formBean? True if so, false otherwise.
354 */
355 public boolean hasErrors()
356 {
357 return (errors != null && (!errors.isEmpty()));
358 }
359
360
361
362 /***
363 * Get the display string of the property with the given name without using a pattern.
364 *
365 * If an object was found for the given property name, it will be formatted with the
366 * formatter found as follows:
367 * 1. look in the ConverterRegistry if a formatter was stored with the fieldname
368 * and optionally locale as key.
369 * 2. if not found, look in the ConverterRegistry if a Converter was stored for the
370 * type of the property that implements Formatter (as well as Converter).
371 * If a formatter was found, it will be used for formatting the property
372 * (using the format(property, pattern) method). If not, ConvertUtils of the
373 * BeanUtils package is used to get the string representation of the property.
374 *
375 * @param name name of the property
376 * @return String the formatted value of the property of the current bean instance
377 * or the registered override value if any was registered.
378 */
379 public String displayProperty(String name)
380 {
381 return displayProperty(name, null);
382 }
383
384 /***
385 * Get the display string of the property with the given name, optionally
386 * using the given pattern.
387 *
388 * If an object was found for the given property name, it will be formatted with the
389 * formatter found as follows:
390 * 1. look in the ConverterRegistry if a formatter was stored with the fieldname
391 * and optionally locale as key.
392 * 2. if not found, look in the ConverterRegistry if a formatter was stored with
393 * the pattern and optionally the locale as key.
394 * 3. if not found, look in the ConverterRegistry if a Converter was stored for the
395 * type of the property that implements Formatter (as well as Converter).
396 * If a formatter was found, it will be used for formatting the property
397 * (using the format(property, pattern) method). If not, ConvertUtils of the
398 * BeanUtils package is used to get the string representation of the property.
399 *
400 * @param name name of the property
401 * @param pattern optional pattern to use for formatting
402 * @return String the formatted value (using the pattern) of the property of the
403 * current bean instance or the registered override value if any was registered.
404 */
405 public String displayProperty(String name, String pattern)
406 {
407 if (name == null)
408 return null;
409
410 String displayString = null;
411
412
413 Map _overrideFields = getOverrideFields();
414 boolean wasOverriden = false;
415 if (_overrideFields != null)
416 {
417 Object storedRawValue = _overrideFields.get(name);
418 String storedValue = null;
419
420 if (storedRawValue != null)
421 {
422 wasOverriden = true;
423 if (storedRawValue instanceof String)
424 {
425 storedValue = (String)storedRawValue;
426 }
427 else if(storedRawValue instanceof String[])
428
429 {
430 storedValue = ValueUtils.convertToString(storedRawValue);
431 }
432 else
433 {
434 try
435 {
436 storedValue = format(name, storedRawValue, pattern);
437 }
438 catch(Exception e)
439 {
440 storedValue = ValueUtils.convertToString(storedRawValue);
441 }
442 }
443 if (storedValue != null)
444 {
445 displayString = storedValue;
446 }
447 }
448
449 if(formattingLog.isDebugEnabled())
450 {
451 formattingLog.debug(
452 "using override for property " + name + ": " + displayString);
453 }
454 }
455
456 if(!wasOverriden)
457 {
458 try
459 {
460 Object _value = Ognl.getValue(name, bean);
461 if (_value != null)
462 {
463
464 displayString = format(name, _value, pattern);
465 }
466 }
467 catch (Exception e)
468 {
469 if(log.isDebugEnabled())
470 {
471 log.error(e.getMessage(), e);
472 }
473 return null;
474 }
475 }
476
477 return displayString;
478 }
479
480 /***
481 * Get the formatter for the given fieldname/ class/ locale.
482 *
483 * 1. look in the ConverterRegistry if a formatter was stored with the formatterName
484 * and optionally locale as key.
485 * 2. if not found, look in the ConverterRegistry if a formatter was stored with
486 * the pattern and optionally the locale as key.
487 * 3. if not found, look in the ConverterRegistry if a Converter was stored for the
488 * type of the property that implements Formatter (as well as Converter).
489 *
490 * @param formatterName name of formatter
491 * @param pattern pattern: might be used as a key to store a Formatter
492 * @param clazz class of property
493 * @param locale locale to get Formatter for
494 * @return Formatter instance of Formatter if found, null otherwise
495 */
496 public Formatter getFormatter(String formatterName, String pattern, Class clazz, Locale locale)
497 {
498 Formatter formatter = null;
499 ConverterRegistry reg = ConverterRegistry.getInstance();
500
501 try
502 {
503
504 if(formatterName != null)
505 {
506 formatter = reg.lookup(formatterName);
507 }
508
509 if(formatter == null && (pattern != null))
510 {
511 formatter = reg.lookup(pattern);
512 }
513
514 if (formatter == null && (clazz != null))
515 {
516 Converter converter = reg.lookup(clazz, getCurrentLocale());
517 if ((converter != null) && (converter instanceof Formatter))
518 {
519 formatter = (Formatter)converter;
520 }
521 }
522 }
523 catch (Exception e)
524 {
525 log.error(e);
526 if(log.isDebugEnabled())
527 {
528 log.error(e.getMessage(), e);
529 }
530
531 }
532
533 return formatter;
534 }
535
536
537
538 /***
539 * Format the given value, independent of the current form, using the class
540 * of the value to lookup a formatter.
541 * @param value value to format
542 * @return String formatted value
543 */
544 public String format(Object value)
545 {
546 return format(null, value, null);
547 }
548
549 /***
550 * Format the given value, independent of the current form, using the class of the
551 * value to lookup a formatter using the provided pattern.
552 * @param value value to format
553 * @param pattern pattern for format
554 * @return String formatted value
555 */
556 public String format(Object value, String pattern)
557 {
558 return format(null, value, pattern);
559 }
560
561 /***
562 * Format the given value, independent of the current form, using the provided
563 * name of the formatter to lookup the formatter or - if no formatter was found -
564 * using the class of the value to lookup the formatter.
565 * @param formatterName name of formatter.
566 * @param value value to format
567 * @return String formatted value
568 */
569 public String format(String formatterName, Object value)
570 {
571 return format(formatterName, value, null);
572 }
573
574 /***
575 * Format the given value, independent of the current form using:
576 * 1. look in the ConverterRegistry if a formatter was stored with the formatterName
577 * and optionally locale as key;
578 * 2. if not found, look in the ConverterRegistry if a formatter was stored with
579 * the pattern and optionally the locale as key;
580 * 3. if not found, look in the ConverterRegistry if a Converter was stored for the
581 * type of the property that implements Formatter (as well as Converter);
582 * @param formatterName name of formatter.
583 * @param value value to format
584 * @param pattern pattern for format
585 * @return String formatted value
586 */
587 public String format(String formatterName, Object value, String pattern)
588 {
589 if(value == null) return null;
590
591 String formatted = null;
592 Formatter formatter =
593 getFormatter(formatterName, pattern, value.getClass(), getCurrentLocale());
594
595 if (formatter != null)
596 {
597 if(formattingLog.isDebugEnabled())
598 {
599 formattingLog.debug(
600 "using formatter " + formatter +
601 " for property " + formatterName);
602 }
603 formatted = formatter.format(value, pattern);
604 }
605 else
606 {
607 if(formattingLog.isDebugEnabled())
608 {
609 formattingLog.debug(
610 "using default convertUtils formatter for property " + formatterName);
611 }
612 formatted = ValueUtils.convertToString(value);
613 }
614 return formatted;
615 }
616
617
618
619 /***
620 * Returns the value to which the attributes map maps the specified key.
621 * Returns null if the map contains no mapping for this key.
622 * @param key key whose associated value is to be returned
623 * @return Object the value to which the attributes map maps the specified key,
624 * or null if the map contains no mapping for this key.
625 * @see java.util.Map#get(java.lang.Object)
626 */
627 public Object get(Object key)
628 {
629 return (attributes != null) ? attributes.get(key) : null;
630 }
631
632 /***
633 * Associates the specified value with the specified key in the attribute map.
634 * If the attribute map previously contained a mapping for this key, the old value
635 * is replaced by the specified value.
636 * @param key key with which the specified value is to be associated.
637 * @param value value to be associated with the specified key.
638 * @see java.util.Map#put(java.lang.Object, java.lang.Object)
639 */
640 public Object put(Object key, Object value)
641 {
642 if(attributes == null) attributes = new HashMap();
643 return attributes.put(key, value);
644 }
645
646 /***
647 * get the attribute values
648 * @return Collection the attribute values or null if no attributes were set
649 * @see java.util.Map#values()
650 */
651 public Collection values()
652 {
653 return (attributes != null) ? attributes.values() : null;
654 }
655
656 /***
657 * get the key set of the attributes
658 * @return Set the key set of the attributes or null if no attributes were set
659 * @see java.util.Map#keySet()
660 */
661 public Set keySet()
662 {
663 return (attributes != null) ? attributes.keySet() : null;
664 }
665
666 /***
667 * clear the attributes
668 * @see java.util.Map#clear()
669 */
670 public void clear()
671 {
672 this.attributes = null;
673 }
674
675 /***
676 * get the number of attributes
677 * @return int the number of attributes
678 * @see java.util.Map#size()
679 */
680 public int size()
681 {
682 return (attributes != null) ? attributes.size() : 0;
683 }
684
685 /***
686 * put the provided map in the attribute map
687 * @param t map to put in attributes
688 * @see java.util.Map#putAll(java.util.Map)
689 */
690 public void putAll(Map t)
691 {
692 if(attributes == null) attributes = new HashMap();
693 attributes.putAll(t);
694 }
695
696 /***
697 * get the entries of the attributes
698 * @return Set the entries of the attributes or null if no attributes were set
699 * @see java.util.Map#entrySet()
700 */
701 public Set entrySet()
702 {
703 return (attributes != null) ? attributes.entrySet() : null;
704 }
705
706 /***
707 * is the provided key the key of an attribute
708 * @param key the key to look for
709 * @return boolean is the provided key the key of an attribute
710 * @see java.util.Map#containsKey(java.lang.Object)
711 */
712 public boolean containsKey(Object key)
713 {
714 return (attributes != null) ? attributes.containsKey(key) : false;
715 }
716
717 /***
718 * are there any attributes
719 * @return boolean are there any attributes
720 * @see java.util.Map#isEmpty()
721 */
722 public boolean isEmpty()
723 {
724 return (attributes != null) ? attributes.isEmpty() : true;
725 }
726
727 /***
728 * remove an attribute
729 * @param key key of the attribute to remove
730 * @return Object the object that was stored under the given key, if any
731 * @see java.util.Map#remove(java.lang.Object)
732 */
733 public Object remove(Object key)
734 {
735 return (attributes != null) ? attributes.remove(key) : null;
736 }
737
738 /***
739 * is the provided value stored as an attribute
740 * @param value the value to look for
741 * @return boolean is the provided value stored as an attribute
742 * @see java.util.Map#containsValue(java.lang.Object value)
743 */
744 public boolean containsValue(Object value)
745 {
746 return (attributes != null) ? attributes.containsValue(value) : false;
747 }
748
749 /***
750 * get current controller
751 * @return FormBeanCtrlBase
752 */
753 public FormBeanCtrlBase getController()
754 {
755 return controller;
756 }
757
758 /***
759 * set current controller
760 * @param controller current controller
761 */
762 public void setController(FormBeanCtrlBase controller)
763 {
764 this.controller = controller;
765 }
766
767 /***
768 * String rep.
769 * @see java.lang.Object#toString()
770 */
771 public String toString()
772 {
773 StringBuffer b = new StringBuffer(super.toString());
774 b.append(" { bean=")
775 .append(bean)
776 .append(", ctrl=")
777 .append(controller);
778 if(attributes != null && (!attributes.isEmpty()))
779 {
780 b.append(", attributes{");
781 for(Iterator i = attributes.keySet().iterator(); i.hasNext(); )
782 {
783 String key = (String)i.next();
784 b.append(key)
785 .append("=")
786 .append(attributes.get(key));
787 if(i.hasNext())
788 {
789 b.append(",");
790 }
791 }
792 b.append("}");
793 }
794 b.append(", errors{");
795 if(errors != null)
796 {
797 for(Iterator i = errors.keySet().iterator(); i.hasNext(); )
798 {
799 String errorKey = (String)i.next();
800 b.append(errorKey)
801 .append("=")
802 .append(getError(errorKey))
803 .append("(override=")
804 .append(getOverrideField(errorKey))
805 .append(")");
806 if(i.hasNext())
807 {
808 b.append(",");
809 }
810 }
811 }
812 b.append("}}");
813 return b.toString();
814 }
815
816 }