1
2
3
4 package org.rcfaces.core.internal.component;
5
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13
14 import javax.el.ValueExpression;
15 import javax.faces.FacesException;
16 import javax.faces.component.UIComponentBase;
17 import javax.faces.context.FacesContext;
18 import javax.faces.convert.Converter;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.rcfaces.core.internal.capability.IComponentEngine;
23 import org.rcfaces.core.internal.capability.IStateChildrenList;
24
25
26
27
28
29 public class BasicComponentEngine extends AbstractComponentEngine {
30 private static final String REVISION = "$Revision: 1.17 $";
31
32 private static final Log LOG = LogFactory
33 .getLog(BasicComponentEngine.class);
34
35 private static final boolean debugEnabled = LOG.isDebugEnabled();
36
37 private static final String VALIDATORS_KEY = "camelia.validators";
38
39 private static final String CONVERTER_ID_PROPERTY = "camalia.converterId";
40
41 static final Class BOOLEAN_CLASS = Boolean.class;
42
43 static final Class STRING_CLASS = String.class;
44
45 private static final Class INTEGER_CLASS = Integer.class;
46
47 private static final Integer INTEGER_0 = new Integer(0);
48
49 private static final Class DOUBLE_CLASS = Double.class;
50
51 private static final Double DOUBLE_0 = new Double(0);
52
53 private static final String[] STRING_EMPTY_ARRAY = new String[0];
54
55 private static final int DATA_ACCESSORS_INIT_SIZE = 2;
56
57 private transient boolean enableDelta;
58
59 private Converter converter;
60
61 private boolean converterSetted = false;
62
63 private Map transientAttributes;
64
65 private Map dataAccessorsByName;
66
67 private IPropertiesManager propertiesManager;
68
69 public BasicComponentEngine(IFactory factory) {
70 super(factory);
71 }
72
73 protected BasicComponentEngine(BasicComponentEngine original) {
74 this(original.factory);
75
76 if (debugEnabled) {
77 LOG.debug("Clone BasicComponentEngine source=" + original);
78 }
79
80 if (original.propertiesManager != null) {
81 propertiesManager = original.propertiesManager.copyOriginalState();
82 }
83
84 if (original.dataAccessorsByName != null
85 && original.dataAccessorsByName.isEmpty() == false) {
86
87 dataAccessorsByName = new HashMap(DATA_ACCESSORS_INIT_SIZE);
88
89 for (Iterator it = original.dataAccessorsByName.entrySet()
90 .iterator(); it.hasNext();) {
91 Map.Entry entry = (Map.Entry) it.next();
92
93 BasicDataAccessor originalDataAccessor = (BasicDataAccessor) entry
94 .getValue();
95
96 BasicDataAccessor newDataAccessor = (BasicDataAccessor) originalDataAccessor
97 .copyOriginalState();
98
99 dataAccessorsByName.put(entry.getKey(), newDataAccessor);
100 }
101 }
102 }
103
104 public final Boolean getBooleanProperty(String propertyName,
105 FacesContext facesContext) {
106 return (Boolean) getInternalProperty(propertyName, BOOLEAN_CLASS,
107 facesContext);
108 }
109
110 public final Integer getIntegerProperty(String propertyName,
111 FacesContext facesContext) {
112 return (Integer) getInternalProperty(propertyName, INTEGER_CLASS,
113 facesContext);
114 }
115
116 public final boolean getBoolProperty(String propertyName,
117 boolean defaultValue, FacesContext facesContext) {
118 Object object = getBooleanProperty(propertyName, facesContext);
119
120 if (object == null) {
121 return defaultValue;
122 }
123
124 if (object == Boolean.FALSE) {
125 return false;
126 }
127
128 if (object == Boolean.TRUE) {
129 return true;
130 }
131
132 return ((Boolean) object).booleanValue();
133 }
134
135 public final String getStringProperty(String propertyName,
136 FacesContext facesContext) {
137 return (String) getInternalProperty(propertyName, STRING_CLASS,
138 facesContext);
139 }
140
141 public final Object getProperty(String propertyName,
142 FacesContext facesContext) {
143 return getInternalProperty(propertyName, null, facesContext);
144 }
145
146 public final int getIntProperty(String propertyName, int defaultValue,
147 FacesContext facesContext) {
148 Integer i = (Integer) getInternalProperty(propertyName, INTEGER_CLASS,
149 facesContext);
150 if (i == null) {
151 return defaultValue;
152 }
153 return i.intValue();
154 }
155
156 public final double getDoubleProperty(String propertyName,
157 double defaultValue, FacesContext facesContext) {
158 Double i = (Double) getInternalProperty(propertyName, DOUBLE_CLASS,
159 facesContext);
160 if (i == null) {
161 return defaultValue;
162 }
163 return i.doubleValue();
164 }
165
166 final Object getLocalProperty(String propertyName) {
167
168 if (debugEnabled) {
169 LOG.debug("getLocalProperty(\"" + propertyName + "\") ...");
170 }
171
172 IPropertiesAccessor propertiesAccessor = getPropertiesAccessor(false);
173 if (propertiesAccessor == null) {
174 if (debugEnabled) {
175 LOG.debug("getLocalProperty(\"" + propertyName
176 + "\") returns null (propertiesAccessor="
177 + propertiesAccessor + ")");
178 }
179 return null;
180 }
181
182 Object value = propertiesAccessor.getProperty(propertyName);
183
184 if (debugEnabled) {
185 LOG.debug("getLocalProperty(\"" + propertyName + "\") returns "
186 + value + " (propertiesAccessor=" + propertiesAccessor
187 + ")");
188 }
189
190 return value;
191 }
192
193 public final Object getInternalProperty(String propertyName,
194 Class requestedClass, FacesContext facesContext) {
195
196 if (debugEnabled) {
197 LOG.debug("getInternalProperty(\""
198 + propertyName
199 + "\" ["
200 + ((requestedClass != null) ? requestedClass.getName()
201 : "no class") + "]) ... (enableDelta="
202 + enableDelta + ")");
203 }
204
205 Object object = getLocalProperty(propertyName);
206 if (object == null) {
207
208 if (debugEnabled) {
209 LOG.debug("getInternalProperty(\""
210 + propertyName
211 + "\" ["
212 + ((requestedClass != null) ? requestedClass.getName()
213 : "no class") + "]) returns null");
214 }
215 return null;
216 }
217
218 if (object instanceof ValueExpression) {
219 ValueExpression valueExpression = (ValueExpression) object;
220
221 if (facesContext == null) {
222 facesContext = getFacesContext();
223 }
224
225 object = valueExpression.getValue(facesContext.getELContext());
226
227 if (debugEnabled) {
228 LOG.debug("getInternalProperty(\""
229 + propertyName
230 + "\" ["
231 + ((requestedClass != null) ? requestedClass.getName()
232 : "no class") + "]) returns a value binding ("
233 + valueExpression + ") => " + object);
234 }
235 return object;
236 }
237
238 if (debugEnabled) {
239 LOG.debug("getInternalProperty(\""
240 + propertyName
241 + "\" ["
242 + ((requestedClass != null) ? requestedClass.getName()
243 : "no class") + "]) returns " + object);
244 }
245
246 return object;
247 }
248
249 protected FacesContext getFacesContext() {
250 FacesContext facesContext = FacesContext.getCurrentInstance();
251 if (facesContext != null) {
252 return facesContext;
253 }
254
255 throw new FacesException("FacesContext is not initialized !", null);
256 }
257
258 protected final void setInternalProperty(String propertyName, Object value) {
259
260 if (debugEnabled) {
261 LOG.debug("setInternalProperty(\"" + propertyName + "\", " + value
262 + ") ...");
263 }
264
265 IPropertiesAccessor pa = getPropertiesAccessor(true);
266
267 pa.setProperty(null, propertyName, value);
268
269 if (debugEnabled) {
270 LOG.debug("setInternalProperty(\"" + propertyName + "\", " + value
271 + ") propertiesAccessor=" + pa);
272 }
273 }
274
275 protected final void setInternalProperty(String propertyName,
276 ValueExpression value) {
277
278 if (debugEnabled) {
279 LOG.debug("setInternalProperty(\"" + propertyName + "\", " + value
280 + ") ...");
281 }
282
283 IPropertiesAccessor pa = getPropertiesAccessor(true);
284
285 pa.setProperty(null, propertyName, value);
286
287 if (debugEnabled) {
288 LOG.debug("setInternalProperty(\"" + propertyName + "\", " + value
289 + ") propertiesAccessor=" + pa);
290 }
291 }
292
293 public final void setProperty(String propertyName, boolean value) {
294 if (value) {
295 setInternalProperty(propertyName, Boolean.TRUE);
296 return;
297 }
298
299 setInternalProperty(propertyName, Boolean.FALSE);
300 }
301
302 public final void setProperty(String propertyName, Boolean bool) {
303 if (bool != null) {
304
305 if (bool.booleanValue()) {
306 bool = Boolean.TRUE;
307
308 } else {
309 bool = Boolean.FALSE;
310 }
311 }
312
313 setInternalProperty(propertyName, bool);
314 }
315
316 public final void setProperty(String propertyName, Integer value) {
317 if (value != null) {
318 if (value.intValue() == 0) {
319 value = INTEGER_0;
320 }
321 }
322
323 setInternalProperty(propertyName, value);
324 }
325
326 public final void setProperty(String propertyName, Double value) {
327 if (value != null) {
328 if (value.doubleValue() == 0.0) {
329 value = DOUBLE_0;
330 }
331 }
332
333 setInternalProperty(propertyName, value);
334 }
335
336 public final void setProperty(String propertyName, double value) {
337 if (value == 0.0) {
338 setInternalProperty(propertyName, DOUBLE_0);
339 return;
340 }
341
342 setInternalProperty(propertyName, new Double(value));
343 }
344
345 public final void setProperty(String propertyName, int value) {
346 if (value == 0) {
347 setInternalProperty(propertyName, INTEGER_0);
348 return;
349 }
350
351 setInternalProperty(propertyName, new Integer(value));
352 }
353
354 public final void setProperty(String propertyName, Object value) {
355 setInternalProperty(propertyName, value);
356 }
357
358 public final void setProperty(String propertyName, ValueExpression value) {
359 setInternalProperty(propertyName, value);
360 }
361
362 final IPropertiesAccessor getPropertiesAccessor(boolean forceDelta) {
363
364 if (debugEnabled) {
365 LOG.debug("getPropertiesAccessor enableDelta=" + enableDelta
366 + " forceDelta=" + forceDelta + " ...");
367 }
368
369 if (propertiesManager != null) {
370 return propertiesManager.getPropertiesAccessor(enableDelta,
371 forceDelta);
372 }
373
374 if (forceDelta == false) {
375 return null;
376 }
377
378 propertiesManager = factory.createPropertiesManager(this);
379
380 if (debugEnabled) {
381 LOG.debug("Create propertiesManager='" + propertiesManager
382 + " for this='" + this + "' enableDelta=" + enableDelta
383 + " forceDelta=" + forceDelta);
384 }
385
386 return propertiesManager.getPropertiesAccessor(enableDelta, forceDelta);
387 }
388
389 public IDataMapAccessor getDataMapAccessor(FacesContext context,
390 String name, boolean forceDelta) {
391 IDataMapAccessor dataAccessor;
392 if (dataAccessorsByName != null) {
393 dataAccessor = (IDataMapAccessor) dataAccessorsByName.get(name);
394 if (dataAccessor != null) {
395 return dataAccessor;
396 }
397 }
398
399 if (forceDelta == false) {
400 return null;
401 }
402
403 dataAccessor = createDataAccessor(context, name);
404 if (dataAccessorsByName == null) {
405 dataAccessorsByName = new HashMap(DATA_ACCESSORS_INIT_SIZE);
406 }
407
408 dataAccessorsByName.put(name, dataAccessor);
409
410 return dataAccessor;
411 }
412
413 protected BasicDataAccessor createDataAccessor(FacesContext context,
414 String name) {
415 return new BasicDataAccessor(name);
416 }
417
418 public void restoreState(FacesContext context, Object state) {
419 Object states[] = (Object[]) state;
420
421 Object props = states[0];
422 if (props != null) {
423 propertiesManager = factory.createPropertiesManager(this);
424 propertiesManager.restoreManagerState(context, props);
425
426 } else {
427 propertiesManager = null;
428 }
429
430 Object datas = states[1];
431 if (datas != null) {
432 Object ds[] = (Object[]) datas;
433
434 dataAccessorsByName = new HashMap(ds.length / 2);
435 for (int i = 0; i < ds.length;) {
436 String name = (String) ds[i++];
437
438 BasicDataAccessor dataAccessor = createDataAccessor(context,
439 name);
440 dataAccessor.setCameliaFactory(factory);
441
442 dataAccessor.restoreDataState(context, ds[i++]);
443
444 dataAccessorsByName.put(name, dataAccessor);
445 }
446 } else {
447 dataAccessorsByName = null;
448 }
449
450 Object converter = states[2];
451 if (converter != null) {
452 this.converterSetted = true;
453 if (Boolean.FALSE.equals(converter)) {
454 this.converter = null;
455
456 } else {
457 this.converter = (Converter) UIComponentBase
458 .restoreAttachedState(context, converter);
459 }
460
461 } else {
462 this.converterSetted = false;
463 this.converter = null;
464 }
465 }
466
467
468
469
470
471
472
473
474 public Object saveState(FacesContext context) {
475 Object states[] = new Object[3];
476
477 if (propertiesManager != null) {
478 states[0] = propertiesManager.saveManagerState(context);
479 }
480
481 if (dataAccessorsByName != null
482 && dataAccessorsByName.isEmpty() == false) {
483 List l = new ArrayList(dataAccessorsByName.size() * 2);
484
485 for (Iterator it = dataAccessorsByName.entrySet().iterator(); it
486 .hasNext();) {
487 Map.Entry entry = (Map.Entry) it.next();
488
489 BasicDataAccessor dataAccessor = (BasicDataAccessor) entry
490 .getValue();
491
492 Object serializedForm = dataAccessor.saveDataState(context);
493
494 if (serializedForm == null) {
495 continue;
496 }
497
498 l.add(entry.getKey());
499 l.add(serializedForm);
500 }
501
502 if (l.isEmpty() == false) {
503 states[1] = l.toArray();
504 }
505 }
506
507 if (converterSetted) {
508 Object savedConverter = Boolean.FALSE;
509 if (converter != null) {
510 savedConverter = UIComponentBase.saveAttachedState(context,
511 converter);
512
513 if (savedConverter == null) {
514 savedConverter = Boolean.FALSE;
515 }
516 }
517
518 states[2] = savedConverter;
519 }
520
521 return states;
522 }
523
524
525
526
527
528
529
530
531 public final void setValue(String valueName, Object value) {
532 setInternalProperty(valueName, value);
533 }
534
535 public final void setValueExpression(String valueName, ValueExpression value) {
536 setProperty(valueName, value);
537 }
538
539 public final Object getValue(String valueName, FacesContext context) {
540 return getInternalProperty(valueName, null, context);
541 }
542
543 public final Object getLocalValue(String valueName) {
544 return getLocalProperty(valueName);
545 }
546
547 public final ValueExpression getValueExpressionProperty(String name) {
548 Object object = getLocalProperty(name);
549 if (object instanceof ValueExpression) {
550 return (ValueExpression) object;
551 }
552
553 return null;
554 }
555
556 public void release() {
557 if (dataAccessorsByName != null
558 && dataAccessorsByName.isEmpty() == false) {
559 for (Iterator it = dataAccessorsByName.values().iterator(); it
560 .hasNext();) {
561 BasicDataAccessor dataAccessor = (BasicDataAccessor) it.next();
562
563 dataAccessor.releaseDatas();
564 }
565
566 dataAccessorsByName = null;
567 }
568
569 if (propertiesManager != null) {
570 propertiesManager.releaseManager();
571 propertiesManager = null;
572 }
573
574 enableDelta = false;
575 converter = null;
576 converterSetted = false;
577 }
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592 public final Converter getConverter(FacesContext facesContext) {
593 if (converterSetted) {
594 return converter;
595 }
596 converterSetted = true;
597
598 if (facesContext == null) {
599 facesContext = getFacesContext();
600 }
601
602 Object converterValue = getProperty(CONVERTER_ID_PROPERTY, facesContext);
603 if (converterValue == null) {
604 return null;
605 }
606
607 if (converterValue instanceof Converter) {
608 converter = (Converter) converterValue;
609
610 return converter;
611 }
612
613 String converterId = String.valueOf(converterValue);
614 if (converterId == null) {
615 return null;
616 }
617
618 converter = facesContext.getApplication().createConverter(converterId);
619
620 return converter;
621 }
622
623 public final void setConverter(Converter converter) {
624 this.converter = converter;
625 this.converterSetted = true;
626
627 if (debugEnabled) {
628 LOG.debug("Set converter: " + converter);
629 }
630 }
631
632 public Object getTransientAttribute(String name) {
633 if (transientAttributes == null) {
634 return null;
635 }
636
637 Object value = transientAttributes.get(name);
638
639 if (debugEnabled) {
640 LOG.debug("Get transient attribute '" + name + "' => " + value);
641 }
642
643 return value;
644 }
645
646 public Object setTransientAttribute(String name, Object value) {
647 if (transientAttributes == null) {
648 transientAttributes = factory.createMap(4);
649 }
650
651 if (debugEnabled) {
652 LOG.debug("Set transient attribute '" + name + "'=" + value);
653 }
654
655 return transientAttributes.put(name, value);
656 }
657
658 public boolean isPropertySetted(String propertyName) {
659
660 if (debugEnabled) {
661 LOG.debug("Is property setted '" + propertyName + "' ...");
662 }
663
664 IPropertiesAccessor propertiesAccessor = getPropertiesAccessor(false);
665 if (propertiesAccessor == null) {
666
667 if (debugEnabled) {
668 LOG.debug("Is property setted '" + propertyName
669 + "' => FALSE (propertiesAccessor='"
670 + propertiesAccessor + "')");
671 }
672 return false;
673 }
674
675 boolean setted = propertiesAccessor.isPropertySetted(propertyName);
676
677 if (debugEnabled) {
678 LOG.debug("Is property setted '" + propertyName + "' => " + setted
679 + " (propertiesAccessor='" + propertiesAccessor + "')");
680 }
681
682 return setted;
683 }
684
685 public void processValidation(FacesContext context) {
686
687 if (debugEnabled) {
688 LOG.debug("Process validation, enableDelta=" + enableDelta);
689 }
690
691 if (enableDelta == false) {
692 return;
693 }
694
695 enableDelta = false;
696 }
697
698 public void processUpdates(FacesContext context) {
699
700 if (debugEnabled) {
701 LOG.debug("Process update, enableDelta=" + enableDelta);
702 }
703
704 enableDelta = false;
705
706 if (enableDelta == true) {
707 throw new FacesException(
708 "Invalid update state without validation phase !");
709 }
710
711 if (dataAccessorsByName != null
712 && dataAccessorsByName.isEmpty() == false) {
713 for (Iterator it = dataAccessorsByName.values().iterator(); it
714 .hasNext();) {
715 BasicDataAccessor dataAccessor = (BasicDataAccessor) it.next();
716
717 dataAccessor.commitDatas(context);
718 }
719 }
720
721 if (propertiesManager != null) {
722 propertiesManager.commitManager(context);
723 }
724 }
725
726 public void startDecodes(FacesContext context) {
727 enableDelta = true;
728
729 if (debugEnabled) {
730 LOG.debug("Start decode component ");
731 }
732 }
733
734
735
736
737
738
739 protected class BasicDataAccessor extends BasicPropertiesManager implements
740 IDataMapAccessor {
741 private static final String REVISION = "$Revision: 1.17 $";
742
743 private final String name;
744
745 private IPropertiesManager propertiesManager;
746
747 public BasicDataAccessor(String name) {
748 this.name = name;
749 }
750
751 final IPropertiesAccessor getDataPropertiesAccessor(boolean forceDelta) {
752 if (propertiesManager != null) {
753 return propertiesManager.getPropertiesAccessor(enableDelta,
754 forceDelta);
755 }
756
757 if (forceDelta == false) {
758 return null;
759 }
760
761 propertiesManager = createPropertiesManager();
762
763 return propertiesManager.getPropertiesAccessor(enableDelta,
764 forceDelta);
765 }
766
767 public IPropertiesManager copyOriginalState() {
768 BasicDataAccessor copy = new BasicDataAccessor(name);
769 copy.propertiesManager = copy;
770 copy.setCameliaFactory(factory);
771 copy.originalPropertiesAccessor = originalPropertiesAccessor;
772
773 return copy;
774 }
775
776 protected IPropertiesManager createPropertiesManager() {
777 return this;
778 }
779
780 public Object removeData(String name, FacesContext facesContext) {
781 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(true);
782
783 return propertiesAccessor.removeProperty(facesContext, name);
784 }
785
786 public Object getData(String key, FacesContext facesContext) {
787 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(false);
788 if (propertiesAccessor == null) {
789 return null;
790 }
791
792 Object object = propertiesAccessor.getProperty(key);
793 if (object == null) {
794 return null;
795 }
796
797 if (object instanceof ValueExpression) {
798 ValueExpression valueBinding = (ValueExpression) object;
799
800 if (facesContext == null) {
801 facesContext = getFacesContext();
802 }
803
804 object = valueBinding.getValue(facesContext.getELContext());
805 if (object == null) {
806 return null;
807 }
808 }
809
810 return object;
811 }
812
813 public Object setData(String name, Object value,
814 FacesContext facesContext) {
815 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(true);
816
817 return propertiesAccessor.setProperty(facesContext, name, value);
818 }
819
820 public void setData(String name, ValueExpression value,
821 FacesContext facesContext) {
822 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(true);
823
824 propertiesAccessor.setProperty(facesContext, name, value);
825 }
826
827 public int getDataCount() {
828 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(false);
829 if (propertiesAccessor == null) {
830 return 0;
831 }
832
833 return propertiesAccessor.size();
834 }
835
836 public String[] listDataKeys(FacesContext facesContext) {
837 IPropertiesAccessor propertiesAccessor = getDataPropertiesAccessor(false);
838 if (propertiesAccessor == null) {
839 return STRING_EMPTY_ARRAY;
840 }
841
842 Collection c = propertiesAccessor.keySet();
843 if (c.isEmpty()) {
844 return STRING_EMPTY_ARRAY;
845 }
846
847 return (String[]) c.toArray(new String[c.size()]);
848 }
849
850 public void restoreDataState(FacesContext context, Object datas) {
851 propertiesManager = factory
852 .createPropertiesManager(BasicComponentEngine.this);
853 propertiesManager.restoreManagerState(context, datas);
854 }
855
856 public Object saveDataState(FacesContext context) {
857 if (propertiesManager == null) {
858 return null;
859 }
860
861 return propertiesManager.saveManagerState(context);
862 }
863
864 public void releaseDatas() {
865 if (propertiesManager == null) {
866 return;
867 }
868
869 propertiesManager.releaseManager();
870 propertiesManager = null;
871 }
872
873 public void commitDatas(FacesContext context) {
874 if (propertiesManager == null) {
875 return;
876 }
877
878 propertiesManager.commitManager(context);
879 }
880
881 public Map getDataMap(FacesContext facesContext) {
882 String keys[] = listDataKeys(facesContext);
883 if (keys.length < 1) {
884 return Collections.EMPTY_MAP;
885 }
886
887 Map map = new HashMap(keys.length);
888
889 for (int i = 0; i < keys.length; i++) {
890 String key = keys[i];
891
892 Object value = getData(key, facesContext);
893
894
895 map.put(key, value);
896 }
897
898 return map;
899 }
900 }
901
902 public IStateChildrenList createStateChildrenList() {
903 return new BasicStateChildrenList();
904 }
905
906 public IComponentEngine copyOriginalState() {
907
908 BasicComponentEngine newComponentEngine = new BasicComponentEngine(this);
909
910 return newComponentEngine;
911 }
912
913 public String toString() {
914 String s = "";
915
916 if (propertiesManager != null) {
917 s += propertiesManager.toString();
918 }
919
920 if (transientAttributes != null
921 && transientAttributes.isEmpty() == false) {
922 if (s.length() > 0) {
923 s += ",";
924 }
925 s += "transient=" + transientAttributes;
926 }
927
928 if (converter != null) {
929 if (s.length() > 0) {
930 s += ",";
931 }
932 s += "converter='" + converter + "'";
933 }
934
935 return s;
936 }
937
938 }