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    
006    package fc.util;
007    
008    import fc.io.*;
009    import fc.util.*;
010    
011    import java.util.*;
012    import java.lang.reflect.*;
013    
014    /**  
015    Makes creating <tt>toString</tt> methods easier. (For example, 
016    provides ability to introspect and write field values). Idea
017    inspired by a similar apache/jakarta utility.
018    <p>
019    Methods of the form append(String, type) imply the name 
020    specified by the string (typically a field name) is shown
021    with value specified by type.
022    <p>
023    Example usage:<br>
024    <tt>foo</tt> and <tt>bar</tt> are fields of this object.
025    <blockquote>
026    <pre>
027    public 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>
035    Another example:<br>
036    Automatically prints this entire object using reflection.
037    <blockquote>
038    <pre>
039    public 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>
047    The class only needs to be instantiated once so here's a more
048    efficient approach:
049    <blockquote>
050    <pre>
051    { //instance initializer
052    ToString tostr = new ToString(this);
053    }
054    public 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    **/
064    public class ToString 
065    {
066    private static  final boolean dbg      = false;
067    private static      Style   defaultStyle = new Style();
068    
069    Object      client;
070    Style     style;
071    StringBuffer  result;
072    boolean     firstFieldDone;
073    
074    /**  
075    Creates a ToString for the specified object, using the
076    default {@link ToString.Style}.
077    **/
078    public ToString(Object obj) {
079      this(obj, (Style)null);
080      }
081    
082    /**  
083    Creates a ToString for the specified object using the
084    specified style
085    @param  obj   the target object
086    @param  style the formatting style
087    **/
088    public 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    /**  
102    Creates a ToString for the specified object with the
103    specified visibility level.
104    
105    @param  obj   the target object
106    @param  style the formatting style
107    **/
108    public 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    /** 
119    Returns the default style object. Changes to this will affect
120    the default formatting
121    **/ 
122    public static ToString.Style getDefaultStyle() 
123      {
124        return defaultStyle;
125        }
126       
127    /** 
128    Sets the style object to use as the default. This style will
129    be used by default by all new instances of ToString.
130    
131    @param   style  the default style
132    **/
133    public static void setDefaultStyle(ToString.Style style) 
134      {
135      Argcheck.notnull(style, "style cannot be null");
136      defaultStyle = style;
137        } 
138    
139    /** 
140    Returns the style being currently used by this instance. 
141    Modifications to this style this will affect rendering
142    output appropriately.
143    **/
144    public ToString.Style getStyle() {
145      return style;
146      }
147      
148    
149    /** Returns the internal buffer used to create the string **/
150    public StringBuffer getBuffer() {
151      return result;
152      }
153    
154    /** 
155    Uses reflection to get the contents of the object. Reflection
156    does not expand and print all the array values even if the
157    current style's {@link ToString.Style#expandArray expandArray}
158    is set to true. To print all array values, use the
159    <tt>append</tt> methods.
160    **/
161    public 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      
173    void reflectImpl(Object obj, Class clazz) 
174    throws 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    
252    void 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    
279    void doEnd(StringBuffer buf) {
280      buf.append(style.endContent);
281      buf.append(style.endString); 
282      }
283    
284    /** Renders the string **/
285    public 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    /** 
295    Returns information about the current state of the ToString
296    object itself. To get the target object's string, use the
297    {@link render} method.
298    **/
299    public String toString() 
300      {
301      return getClass().getName() + "; using: " + style;
302      }
303      
304    /** Unit test **/
305    public 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    
390    private static class TestClass 
391    {
392    private Style   style;
393    private boolean useReflection;
394    
395    TestClass(Style style, boolean useReflection) { 
396      this.style = style; 
397      this.useReflection = useReflection;
398      }
399    
400    static int staticInt = 10;
401    public  static String staticString = "staticString";
402          
403    public    String   pubString    = "publicString";
404    protected   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();
410    private   String   privateString  = "privateStrng";       
411    
412    public 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    /** 
427    Drives the formatting behavior. Behavior different than 
428    the defaults can be achieved by instantiating a new object
429    and setting it's properties appropriately.
430    **/
431    public 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    /** 
547    Appends an arbitrary string to the result. This can be
548    used as a prologue, epilogue etc. 
549    **/
550    public ToString append(Object str) 
551      {
552      result.append(str);
553      return this;
554      }
555      
556    public 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    
571    public 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
587    public 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    
601    public 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    
615    public 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    
629    public 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    
643    public 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    
657    public 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    
671    public 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    
685    public 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
700    public 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    
728    public 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    
756    public 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    
784    public 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    
812    public 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    
840    public 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      
868    public 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      
896    public 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    
924    public 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