001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002// The Molly framework is freely distributable under the terms of an
003// MIT-style license. For details, see the molly pages web site at:
004// http://www.mollypages.org/. Use, modify, have fun !
005
006package fc.util;
007
008import fc.io.*;
009import fc.util.*;
010
011import java.util.*;
012import java.lang.reflect.*;
013
014/**  
015Makes creating <tt>toString</tt> methods easier. (For example, 
016provides ability to introspect and write field values). Idea
017inspired by a similar apache/jakarta utility.
018<p>
019Methods of the form append(String, type) imply the name 
020specified by the string (typically a field name) is shown
021with value specified by type.
022<p>
023Example usage:<br>
024<tt>foo</tt> and <tt>bar</tt> are fields of this object.
025<blockquote>
026<pre>
027public String toString() {
028  return new ToString(this).
029    <font color="blue">append</font>("foo","some-value").
030    <font color="blue">append</font>("bar",123).
031    <font color="blue">render</font>();
032  }
033</pre>
034</blockquote>
035Another example:<br>
036Automatically prints this entire object using reflection.
037<blockquote>
038<pre>
039public String toString() {
040  return new ToString(this).<font color="blue">reflect</font>().
041  <font color="blue">render</font>();
042  }
043</pre>
044</blockquote>
045<i><b>Note</b>: Don't forget the <tt>render()</tt> call at the end.</i>
046<p>
047The class only needs to be instantiated once so here's a more
048efficient approach:
049<blockquote>
050<pre>
051{ //instance initializer
052ToString tostr = new ToString(this);
053}
054public String toString() {
055  return tostr.<font color="blue">reflect</font>().
056  <font color="blue">render</font>();
057  }
058</pre>
059</blockquote>
060<p>
061
062@author hursh jain
063**/
064public class ToString 
065{
066private static  final boolean dbg      = false;
067private static      Style   defaultStyle = new Style();
068
069Object      client;
070Style     style;
071StringBuffer  result;
072boolean     firstFieldDone;
073
074/**  
075Creates a ToString for the specified object, using the
076default {@link ToString.Style}.
077**/
078public ToString(Object obj) {
079  this(obj, (Style)null);
080  }
081
082/**  
083Creates a ToString for the specified object using the
084specified style
085@param  obj   the target object
086@param  style the formatting style
087**/
088public ToString(Object obj, Style style) 
089  {
090  Argcheck.notnull(obj, "target object cannot be null");
091  client = obj;
092  
093  if (style == null)
094    this.style = defaultStyle;
095  else
096    this.style = style;
097    
098  result = new StringBuffer();
099  }
100
101/**  
102Creates a ToString for the specified object with the
103specified visibility level.
104
105@param  obj   the target object
106@param  style the formatting style
107**/
108public ToString(Object obj, Style.VisibleLevel level) 
109  {
110  Argcheck.notnull(obj, "target object cannot be null");
111  client = obj;
112  this.style = defaultStyle;
113  this.style.reflectVisibleLevel = level;
114  result = new StringBuffer();
115  }
116
117
118/** 
119Returns the default style object. Changes to this will affect
120the default formatting
121**/ 
122public static ToString.Style getDefaultStyle() 
123  {
124    return defaultStyle;
125    }
126   
127/** 
128Sets the style object to use as the default. This style will
129be used by default by all new instances of ToString.
130
131@param   style  the default style
132**/
133public static void setDefaultStyle(ToString.Style style) 
134  {
135  Argcheck.notnull(style, "style cannot be null");
136  defaultStyle = style;
137    } 
138
139/** 
140Returns the style being currently used by this instance. 
141Modifications to this style this will affect rendering
142output appropriately.
143**/
144public ToString.Style getStyle() {
145  return style;
146  }
147  
148
149/** Returns the internal buffer used to create the string **/
150public StringBuffer getBuffer() {
151  return result;
152  }
153
154/** 
155Uses reflection to get the contents of the object. Reflection
156does not expand and print all the array values even if the
157current style's {@link ToString.Style#expandArray expandArray}
158is set to true. To print all array values, use the
159<tt>append</tt> methods.
160**/
161public ToString reflect() 
162  {
163  try {
164    reflectImpl(client, client.getClass());
165    }
166  catch (IllegalAccessException e) {
167    result.append("Cannot convert to string using reflection");
168    result.append(e.toString());
169    }
170  return this;
171  }
172  
173void reflectImpl(Object obj, Class clazz) 
174throws IllegalAccessException
175  {
176  if (dbg) System.out.println("reflectImpl("+clazz+")");
177  
178  Field[] fields = clazz.getDeclaredFields();
179  
180  if (dbg) System.out.println("got declared fields: " + Arrays.asList(fields));
181  
182  //need this call, otherwise getting the fields does not work
183  Field.setAccessible(fields, true);
184  
185  for(int n = 0; n < fields.length; n++) 
186    {
187    //if (dbg) System.out.println("declaredField["+n+"]; '"+fields[n].getName()+"'="+fields[n].get(obj));   
188    
189    Field f = fields[n];
190    int mod = f.getModifiers();
191
192    if (dbg) System.out.println(f.getName() +" modifier="+mod);
193
194    if (! style.reflectStatics && Modifier.isStatic(mod)) {
195      if (dbg) System.out.println("reflectStatics = false, ignoring static field: " + f.getName());
196      continue;
197      }
198    
199    boolean defaultVis = ! (Modifier.isPublic(mod) 
200        || Modifier.isProtected(mod) 
201        || Modifier.isPrivate(mod));
202          
203
204    if (style.ignoredFieldNames.contains(f.getName().toLowerCase())) {
205      if (dbg) System.out.println("ignoring: " + f.getName() + " [in ingorelist]");     
206      continue;
207      }
208
209    if (style.reflectVisibleLevel==Style.VisibleLevel.PUBLIC)
210      {
211      if (Modifier.isPublic(mod)) { 
212        append(f.getName(), f.get(obj));
213        } 
214      }
215    else if (style.reflectVisibleLevel==Style.VisibleLevel.PROTECTED)
216      {
217      if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) {
218        append(f.getName(), f.get(obj));
219        }     
220      }
221    else if (style.reflectVisibleLevel==Style.VisibleLevel.DEFAULT)
222      {
223      if (Modifier.isPublic(mod) || Modifier.isProtected(mod)
224          || defaultVis) {
225        append(f.getName(), f.get(obj));
226        }     
227      }     
228    else if (style.reflectVisibleLevel==Style.VisibleLevel.PRIVATE)
229      {
230      if (Modifier.isPublic(mod) || Modifier.isProtected(mod)
231          || Modifier.isPrivate(mod) || defaultVis) {
232        append(f.getName(), f.get(obj));
233        }     
234      }
235    else
236      {
237      System.out.println("ERROR in ToString().reflecImpl(), this should not happen, toString won't be accurate");
238      }
239      
240    if (dbg) System.out.println("buf=>>"+result+"<<");
241    } //~for 
242
243  if (style.reflectSuperClass) {
244    Class superclazz = clazz.getSuperclass();
245    if (superclazz != null && superclazz != Object.class)
246      reflectImpl(obj, superclazz);
247    }
248  
249  } //reflectImpl
250
251
252void doStart(StringBuffer buf)
253  {
254  buf.append(style.startString);
255
256  Class clazz = client.getClass();
257  String name = clazz.getName();
258  
259  if (style.className)
260    {
261    if (style.fullClassName) {
262      buf.append(name);
263      }
264    else {
265      int last = name.lastIndexOf(".");
266      last = (last < 0) ? 0 : last + 1; //+1 to skip '.' itself
267      buf.append( name.substring(last, name.length()) );
268      }
269    }
270    
271  if (style.idHashCode) {
272    buf.append("@");
273    buf.append(System.identityHashCode(client));
274    } 
275
276  buf.append(style.startContent);
277  }
278
279void doEnd(StringBuffer buf) {
280  buf.append(style.endContent);
281  buf.append(style.endString); 
282  }
283
284/** Renders the string **/
285public String render() 
286  {
287  StringBuffer finalResult = new StringBuffer(result.length() + 128);
288  doStart(finalResult);
289  finalResult.append(result);
290  doEnd(finalResult); 
291  return finalResult.toString();
292  }
293
294/** 
295Returns information about the current state of the ToString
296object itself. To get the target object's string, use the
297{@link render} method.
298**/
299public String toString() 
300  {
301  return getClass().getName() + "; using: " + style;
302  }
303  
304/** Unit test **/
305public static void main(String[] args)
306  { 
307  System.out.println("=== Test using reflection ===");
308  
309  Style style = null;
310  
311  style = new Style();
312  style.ignoreFieldName("style");
313  style.reflectVisibleLevel = Style.VisibleLevel.PUBLIC;
314  style.reflectStatics = true;
315  System.out.println("public fields [including statics]");
316  System.out.println(new TestClass(style, true));
317  System.out.println("");
318    
319  style = new Style(); 
320  style.ignoreFieldName("style");
321  style.reflectVisibleLevel = Style.VisibleLevel.PUBLIC;
322  System.out.println("public fields only");
323  System.out.println(new TestClass(style, true));
324  System.out.println("");
325
326  style = new Style(); 
327  style.ignoreFieldName("style");
328  style.reflectVisibleLevel = Style.VisibleLevel.PUBLIC;
329  style.idHashCode = false;
330  style.className = false;
331  System.out.println("public fields only, no id ref or class name");
332  System.out.println(new TestClass(style, true));
333  System.out.println("");
334  
335  style = new Style();
336  style.ignoreFieldName("style");
337  style.reflectVisibleLevel = Style.VisibleLevel.PROTECTED;
338  System.out.println("protected and higher");
339  System.out.println(new TestClass(style, true));
340  System.out.println("");
341  
342  style = new Style();
343  style.ignoreFieldName("style");
344  style.reflectStatics = true;
345  style.reflectVisibleLevel = Style.VisibleLevel.DEFAULT;
346  System.out.println("package and higher [including statics]");
347  System.out.println(new TestClass(style, true));
348  System.out.println("");
349
350  style = new Style();
351  style.ignoreFieldName("style");
352  style.reflectVisibleLevel = Style.VisibleLevel.DEFAULT;
353  style.expandArrays = true;
354  System.out.println("package and higher fields, arrays EXPANDED");
355  System.out.println(new TestClass(style, true));
356  System.out.println("");
357  
358  style = new Style();
359  style.ignoreFieldName("style");
360  style.reflectVisibleLevel = Style.VisibleLevel.PRIVATE;
361  System.out.println("private (all) fields");
362  System.out.println(new TestClass(style, true));
363  System.out.println("");
364  
365  style = new Style();  
366  style.ignoreFieldName("style");
367  System.out.println("default style output");
368  System.out.println(new TestClass(style, true));
369  System.out.println("");
370  
371  System.out.println("==== Test without reflection ====");
372  style = new Style();  
373  System.out.println(new TestClass(style, false));
374  System.out.println("");
375  
376  System.out.println("With no field names");
377  style = new Style();  
378  style.fieldName = false;
379  System.out.println(new TestClass(style, false));
380  System.out.println("");
381  
382  style = new Style();  
383  style.reflectVisibleLevel = Style.VisibleLevel.DEFAULT;
384  style.expandArrays = true;
385  System.out.println("Expanded arrays");
386  System.out.println(new TestClass(style, false));
387  System.out.println("");
388  }
389
390private static class TestClass 
391{
392private Style   style;
393private boolean useReflection;
394
395TestClass(Style style, boolean useReflection) { 
396  this.style = style; 
397  this.useReflection = useReflection;
398  }
399
400static int staticInt = 10;
401public  static String staticString = "staticString";
402      
403public    String   pubString    = "publicString";
404protected   String   protectedString = "protectedString";
405      String   defString    = "defaultString";
406      int[]    intArray   = new int[] { 1, 2, 3};
407      double[] doubleArray  = new double[] {1.3, 2.6, 3.9};
408      Object[] objectArray  = new Object[] { null, new Object() };
409      List   someList   = new ArrayList();
410private   String   privateString  = "privateStrng";       
411
412public String toString() 
413  {
414  if (useReflection)
415    return new ToString(this, style).reflect().render();
416  else {
417    return new ToString(this, style).
418      append("intArray", intArray).
419      append("doubleArray", doubleArray).
420      append("pubString", pubString).render();
421    }
422  }
423} //~TestClass
424
425
426/** 
427Drives the formatting behavior. Behavior different than 
428the defaults can be achieved by instantiating a new object
429and setting it's properties appropriately.
430**/
431public static class Style 
432  { 
433  private List ignoredFieldNames = new ArrayList();
434
435  /**
436  Case insensitive field names that will be ignored (for example a
437  public field "foo" may be printed otherwise, but if added to this
438  list, it would be ignored). Use this method to add as many
439  field names as needed.
440  **/
441  public void ignoreFieldName(String name) {
442    ignoredFieldNames.add(name.toLowerCase());
443    }
444  
445  /**
446   The start of the entire string. default to empty: <tt>""</tt> 
447  **/
448  public String startString =     "";
449
450  /** 
451  The end of the entire string. default to empty: <tt>""</tt> 
452  **/
453  public String endString =       ""        ;
454
455  /** 
456  The start of the string <b>after</b> the object classname and
457  identity reference. default: <tt>[</tt> 
458  **/
459  public String startContent =    "["       ;
460
461  /** 
462  The end of the string <b>after</b> the object classname and
463  identity reference. default: <tt>]</tt> 
464  **/
465  public String endContent =      "]";
466
467  /** default: <tt>=</tt> **/
468  public String fieldAndValSep =    "=";
469
470  /** default: <tt>,</tt> **/
471  public String fieldSep =      ", ";
472
473  /** default: <tt>{</tt> **/
474  public String startArray =      "{";
475
476  /** default: <tt>}</tt> **/
477  public String endArray =      "}";
478
479  /** default: <tt>,</tt> **/
480  public String arrayValSep =     ",";
481  
482  /** 
483  Expand array values, default: <tt>false</tt>. <bNote:</b> 
484  expansion only works when arrays are manually added using
485  one of the <tt>append(..)</tt> methods, not when using
486  reflection.
487  **/
488  public boolean expandArrays = false;
489  
490  /** Print the field name ? default: <tt>true</tt>. If 
491  field names are <b>not</b> printed, then neither is
492  the {@link #FieldAndValSep} - only the value of a field
493  is printed.
494  **/
495  public boolean fieldName = true;
496
497  /** Print the class name at all ? default: <tt>true</tt> **/
498  public boolean className = true;
499  
500  /** print full class name ? default: <tt>false</tt> **/
501  public boolean fullClassName = false;
502    
503  /** print indentity hash code for the object ? default: <tt>true</tt> **/
504  public boolean idHashCode =   true;
505    
506  /** print field names when using reflection ? default: <tt>true</tt>**/
507  public boolean reflectFieldName = true;
508
509  /** 
510  Prints the superclass's variables when using reflection ? default: <tt>false</tt>
511  **/
512  public boolean reflectSuperClass = false;
513
514  /** 
515  Reflects static variables. By default this is <tt>false</tt> 
516  since statics are not part of the object's instance-state.
517  **/
518  public boolean reflectStatics = false;
519
520  /** 
521  Default access level when using reflection (fields with
522  this or looser access will be printed). default: PRIVATE (EVERYTHING
523  IS PRINTED)
524  **/
525  public VisibleLevel reflectVisibleLevel = VisibleLevel.PRIVATE;
526  
527  public static final class VisibleLevel 
528    {
529    private VisibleLevel() { }
530    public static VisibleLevel PUBLIC = new VisibleLevel ();
531    public static VisibleLevel PROTECTED = new VisibleLevel ();
532    public static VisibleLevel DEFAULT = new VisibleLevel ();
533    public static VisibleLevel PRIVATE = new VisibleLevel ();
534    }     
535  
536  public Style() { }
537  public String toString() {
538    return new ToString(this).reflect().render();  
539    }
540    
541  } //~class Style
542
543
544//==appends===========================================
545
546/** 
547Appends an arbitrary string to the result. This can be
548used as a prologue, epilogue etc. 
549**/
550public ToString append(Object str) 
551  {
552  result.append(str);
553  return this;
554  }
555  
556public ToString append(String fieldName, Object val) 
557  {
558  if (firstFieldDone)
559    result.append(style.fieldSep);
560
561  if (style.fieldName) { 
562    result.append(fieldName);
563    result.append(style.fieldAndValSep); 
564    } 
565  result.append(val);
566
567  firstFieldDone = true;
568  return this;
569    }
570
571public ToString append(String fieldName, String val) 
572  {
573  if (firstFieldDone)
574    result.append(style.fieldSep);
575
576  if (style.fieldName) { 
577    result.append(fieldName);
578    result.append(style.fieldAndValSep); 
579    } 
580  result.append(val);
581
582  firstFieldDone = true;
583  return this;
584    }
585
586//Primitive Types
587public ToString append(String fieldName, long val) 
588  {
589  if (firstFieldDone)
590    result.append(style.fieldSep);
591  if (style.fieldName) { 
592    result.append(fieldName);
593    result.append(style.fieldAndValSep); 
594    }
595  result.append(val);
596
597  firstFieldDone = true;
598  return this;
599    }
600
601public ToString append(String fieldName, int val) 
602  {
603  if (firstFieldDone)
604    result.append(style.fieldSep);
605  if (style.fieldName) { 
606    result.append(fieldName);
607    result.append(style.fieldAndValSep); 
608    }
609  result.append(val);
610
611  firstFieldDone = true;
612  return this;
613    }
614
615public ToString append(String fieldName, short val) 
616  {
617  if (firstFieldDone)
618    result.append(style.fieldSep);
619  if (style.fieldName) { 
620    result.append(fieldName);
621    result.append(style.fieldAndValSep); 
622    }
623  result.append(val);
624
625  firstFieldDone = true;
626  return this;
627    }
628
629public ToString append(String fieldName, byte val) 
630  {
631  if (firstFieldDone)
632    result.append(style.fieldSep);
633  if (style.fieldName) { 
634    result.append(fieldName);
635    result.append(style.fieldAndValSep); 
636    }
637  result.append(val);
638
639  firstFieldDone = true;
640  return this;
641    }
642
643public ToString append(String fieldName, double val) 
644  {
645  if (firstFieldDone)
646    result.append(style.fieldSep);
647  if (style.fieldName) { 
648    result.append(fieldName);
649    result.append(style.fieldAndValSep); 
650    }
651  result.append(val);
652
653  firstFieldDone = true;
654  return this;
655    }
656
657public ToString append(String fieldName, float val) 
658  {
659  if (firstFieldDone)
660    result.append(style.fieldSep);
661  if (style.fieldName) { 
662    result.append(fieldName);
663    result.append(style.fieldAndValSep); 
664    }
665  result.append(val);
666
667  firstFieldDone = true;
668  return this;
669    }
670
671public ToString append(String fieldName, char val) 
672  {
673  if (firstFieldDone)
674    result.append(style.fieldSep);
675  if (style.fieldName) { 
676    result.append(fieldName);
677    result.append(style.fieldAndValSep); 
678    }
679  result.append(val);
680
681  firstFieldDone = true;
682  return this;
683    }
684
685public ToString append(String fieldName, boolean val) 
686  {
687  if (firstFieldDone)
688    result.append(style.fieldSep);
689  if (style.fieldName) { 
690    result.append(fieldName);
691    result.append(style.fieldAndValSep); 
692    }
693  result.append(val);
694  
695  firstFieldDone = true;
696  return this;
697    }
698
699//Array types
700public ToString append(String fieldName, Object[] val) 
701  {
702  if (firstFieldDone)
703    result.append(style.fieldSep);
704    
705  if (style.fieldName) { 
706    result.append(fieldName);
707    result.append(style.fieldAndValSep); 
708    }
709  
710  if (style.expandArrays) 
711    {
712    result.append(style.startArray);
713    for (int n = 0; n < val.length; n++) 
714      {
715      if (n != 0) 
716        result.append(style.arrayValSep);
717      result.append(val[n]);
718      }
719    result.append(style.endArray);
720    }
721  else
722    result.append(val);
723
724  firstFieldDone = true;
725  return this;
726    }
727
728public ToString append(String fieldName, long[] val) 
729  {
730  if (firstFieldDone)
731    result.append(style.fieldSep);
732    
733  if (style.fieldName) { 
734    result.append(fieldName);
735    result.append(style.fieldAndValSep); 
736    }
737  
738  if (style.expandArrays) 
739    {
740    result.append(style.startArray);
741    for (int n = 0; n < val.length; n++) 
742      {
743      if (n != 0) 
744        result.append(style.arrayValSep);
745      result.append(val[n]);
746      }
747    result.append(style.endArray);
748    }
749  else
750    result.append(val);
751
752  firstFieldDone = true;
753  return this;
754    }
755
756public ToString append(String fieldName, int[] val) 
757  {
758  if (firstFieldDone)
759    result.append(style.fieldSep);
760    
761  if (style.fieldName) { 
762    result.append(fieldName);
763    result.append(style.fieldAndValSep); 
764    }
765  
766  if (style.expandArrays) 
767    {
768    result.append(style.startArray);
769    for (int n = 0; n < val.length; n++) 
770      {
771      if (n != 0) 
772        result.append(style.arrayValSep);
773      result.append(val[n]);
774      }
775    result.append(style.endArray);
776    }
777  else
778    result.append(val);
779
780  firstFieldDone = true;
781  return this;
782    }
783
784public ToString append(String fieldName, short[] val) 
785  {
786  if (firstFieldDone)
787    result.append(style.fieldSep);
788    
789  if (style.fieldName) { 
790    result.append(fieldName);
791    result.append(style.fieldAndValSep); 
792    }
793  
794  if (style.expandArrays) 
795    {
796    result.append(style.startArray);
797    for (int n = 0; n < val.length; n++) 
798      {
799      if (n != 0) 
800        result.append(style.arrayValSep);
801      result.append(val[n]);
802      }
803    result.append(style.endArray);
804    }
805  else
806    result.append(val);
807
808  firstFieldDone = true;
809  return this;
810    }
811
812public ToString append(String fieldName, byte[] val) 
813  {
814  if (firstFieldDone)
815    result.append(style.fieldSep);
816    
817  if (style.fieldName) { 
818    result.append(fieldName);
819    result.append(style.fieldAndValSep); 
820    }
821  
822  if (style.expandArrays) 
823    {
824    result.append(style.startArray);
825    for (int n = 0; n < val.length; n++) 
826      {
827      if (n != 0) 
828        result.append(style.arrayValSep);
829      result.append(val[n]);
830      }
831    result.append(style.endArray);
832    }
833  else
834    result.append(val);
835
836  firstFieldDone = true;
837  return this;
838    }
839
840public ToString append(String fieldName, char[] val) 
841  {
842  if (firstFieldDone)
843    result.append(style.fieldSep);
844    
845  if (style.fieldName) { 
846    result.append(fieldName);
847    result.append(style.fieldAndValSep); 
848    }
849  
850  if (style.expandArrays) 
851    {
852    result.append(style.startArray);
853    for (int n = 0; n < val.length; n++) 
854      {
855      if (n != 0) 
856        result.append(style.arrayValSep);
857      result.append(val[n]);
858      }
859    result.append(style.endArray);
860    }
861  else
862    result.append(val);
863
864  firstFieldDone = true;
865  return this;
866    }
867  
868public ToString append(String fieldName, double[] val) 
869  {
870  if (firstFieldDone)
871    result.append(style.fieldSep);
872    
873  if (style.fieldName) { 
874    result.append(fieldName);
875    result.append(style.fieldAndValSep); 
876    }
877  
878  if (style.expandArrays) 
879    {
880    result.append(style.startArray);
881    for (int n = 0; n < val.length; n++) 
882      {
883      if (n != 0) 
884        result.append(style.arrayValSep);
885      result.append(val[n]);
886      }
887    result.append(style.endArray);
888    }
889  else
890    result.append(val);
891
892  firstFieldDone = true;
893  return this;
894    }
895  
896public ToString append(String fieldName, float[] val) 
897  {
898  if (firstFieldDone)
899    result.append(style.fieldSep);
900    
901  if (style.fieldName) { 
902    result.append(fieldName);
903    result.append(style.fieldAndValSep); 
904    }
905  
906  if (style.expandArrays) 
907    {
908    result.append(style.startArray);
909    for (int n = 0; n < val.length; n++) 
910      {
911      if (n != 0) 
912        result.append(style.arrayValSep);
913      result.append(val[n]);
914      }
915    result.append(style.endArray);
916    }
917  else
918    result.append(val);
919
920  firstFieldDone = true;
921  return this;
922    }
923
924public ToString append(String fieldName, boolean[] val) 
925  {
926  if (firstFieldDone)
927    result.append(style.fieldSep);
928    
929  if (style.fieldName) { 
930    result.append(fieldName);
931    result.append(style.fieldAndValSep); 
932    }
933  
934  if (style.expandArrays) 
935    {
936    result.append(style.startArray);
937    for (int n = 0; n < val.length; n++) 
938      {
939      if (n != 0) 
940        result.append(style.arrayValSep);
941      result.append(val[n]);
942      }
943    result.append(style.endArray);
944    }
945  else
946    result.append(val);
947
948  firstFieldDone = true;
949  return this;
950    }
951
952//==end appends============================================ 
953
954}          //~class ToString