001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.fulcrum.yaafi.interceptor.util;
021    
022    import java.io.PrintWriter;
023    import java.io.StringWriter;
024    import java.util.Collection;
025    import java.util.Dictionary;
026    import java.util.Iterator;
027    
028    import org.apache.fulcrum.yaafi.framework.util.StringUtils;
029    
030    /**
031     * Creates a string representation of method argument.
032     *
033     * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
034     */
035    public class ArgumentToStringBuilderImpl implements InterceptorToStringBuilder
036    {
037        /** include the class name in the result */
038        public static final int INCLUDE_CLASSNAME = 0x1;
039    
040        /** include the hashcode in the result */
041        public static final int INCLUDE_HASHCODE = 0x02;
042    
043        /** the default mode using class names and hashcode */
044        private static int defaultMode = INCLUDE_CLASSNAME & INCLUDE_HASHCODE;
045    
046        /** our current formatting mode */
047        private int mode;
048    
049        /** the maximum length of a dumped argument */
050        private static final int MAX_LINE_LENGTH = 2000;
051    
052        /** seperator for the arguments in the logfile */
053        private static final char SEPERATOR = ';';
054    
055        /** the output for a NULL value **/
056        private static final String NULL_STRING = "<null>";
057    
058        /** the output for a length string **/
059        private static final String LENGTH_STRING = "length=";
060    
061        /** the output for a value string **/
062        private static final String VALUE_STRING = "value=";
063    
064        /** maximum line length for dumping arguments */
065        private int maxArgLength;
066    
067        /** the result of the invocation */
068        private StringBuffer buffer;
069    
070        /** the target object */
071        private Object target;
072    
073        /**
074         * Constructor
075         */
076        public ArgumentToStringBuilderImpl()
077        {
078            this.mode = ArgumentToStringBuilderImpl.defaultMode;
079            this.maxArgLength = MAX_LINE_LENGTH;
080            this.buffer = new StringBuffer();
081        }
082    
083        /**
084         * Constructor
085         *
086         * @param target the object to print
087         */
088        public ArgumentToStringBuilderImpl(Object target)
089        {
090            this(target,MAX_LINE_LENGTH);
091        }
092    
093        /**
094         * Constructor
095         *
096         * @param target the object to print
097         * @param maxArgLength the maximum length
098         */
099        public ArgumentToStringBuilderImpl(Object target, int maxArgLength)
100        {
101            this(target,
102                maxArgLength,
103                ArgumentToStringBuilderImpl.defaultMode
104                );
105        }
106    
107        /**
108         * Constructor
109         *
110         * @param target the object to print
111         * @param maxArgLength the maximum length
112         * @param mode the formatting mode to use
113         */
114        public ArgumentToStringBuilderImpl(Object target, int maxArgLength, int mode)
115        {
116            this.buffer = new StringBuffer();
117            this.target = target;
118            this.maxArgLength = maxArgLength;
119            this.mode = mode;
120        }
121    
122        /**
123         * @see org.apache.fulcrum.yaafi.interceptor.util.InterceptorToStringBuilder#setMaxArgLength(int)
124         */
125        public void setMaxArgLength(int maxArgLength)
126        {
127            this.maxArgLength = maxArgLength;
128        }
129    
130        /**
131         * @see org.apache.fulcrum.yaafi.interceptor.util.InterceptorToStringBuilder#setTarget(java.lang.Object)
132         */
133        public void setTarget(Object target)
134        {
135            this.target = target;
136        }
137    
138        /**
139         * @see org.apache.fulcrum.yaafi.interceptor.util.InterceptorToStringBuilder#setMode(int)
140         */
141        public void setMode(int mode)
142        {
143            this.mode = mode;
144        }
145    
146        /**
147         * @return Returns the mode.
148         */
149        public int getMode()
150        {
151            return this.mode;
152        }
153    
154            /**
155         * @see java.lang.Object#toString()
156         */
157        public String toString()
158        {
159            try
160            {
161                if( this.target == null )
162                {
163                    this.buffer.append(NULL_STRING);
164                }
165                else if( this.target instanceof Object[] )
166                {
167                    this.appendClassName(target);
168                    this.appendHashCode(target);
169                    this.appendChar('[');
170                    this.append( this.toString((Object[]) this.target) );
171                    this.appendChar(']');
172                }
173                else if( this.target instanceof boolean[] )
174                {
175                    this.appendClassName(target);
176                    this.appendHashCode(target);
177                    this.appendChar('[');
178                    this.append( this.toString((boolean[]) this.target) );
179                    this.appendChar(']');
180                }
181                else if( this.target instanceof char[] )
182                {
183                    this.appendClassName(target);
184                    this.appendHashCode(target);
185                    this.appendChar('[');
186                    this.append( this.toString((char[]) this.target) );
187                    this.appendChar(']');
188                }
189                else if( this.target instanceof byte[] )
190                {
191                    this.appendClassName(target);
192                    this.appendHashCode(target);
193                    this.appendChar('[');
194                    this.append( this.toString((byte[]) this.target) );
195                    this.appendChar(']');
196                }
197                else if( this.target instanceof short[] )
198                {
199                    this.appendClassName(target);
200                    this.appendHashCode(target);
201                    this.appendChar('[');
202                    this.append( this.toString((short[]) this.target) );
203                    this.appendChar(']');
204                }
205                else if( this.target instanceof int[] )
206                {
207                    this.appendClassName(target);
208                    this.appendHashCode(target);
209                    this.appendChar('[');
210                    this.append( this.toString((int[]) this.target) );
211                    this.appendChar(']');
212                }
213                else if( this.target instanceof long[] )
214                {
215                    this.appendClassName(target);
216                    this.appendHashCode(target);
217                    this.appendChar('[');
218                    this.append( this.toString((long[]) this.target) );
219                    this.appendChar(']');
220                }
221                else if( this.target instanceof float[] )
222                {
223                    this.appendClassName(target);
224                    this.appendHashCode(target);
225                    this.appendChar('[');
226                    this.append( this.toString((float[]) this.target) );
227                    this.appendChar(']');
228                }
229                else if( this.target instanceof double[] )
230                {
231                    this.appendClassName(target);
232                    this.appendHashCode(target);
233                    this.appendChar('[');
234                    this.append( this.toString((double[]) this.target) );
235                    this.appendChar(']');
236                }
237                else if( this.target instanceof String )
238                {
239                    this.appendClassName(target);
240                    this.appendHashCode(target);
241                    this.appendChar('[');
242                    this.append( this.toString((String) this.target) );
243                    this.appendChar(']');
244                }
245                else if( this.target instanceof Collection )
246                {
247                    this.appendClassName(target);
248                    this.appendHashCode(target);
249                    this.appendChar('[');
250                    this.append( this.toString((Collection) this.target) );
251                    this.appendChar(']');
252                }
253                else if( this.target instanceof Dictionary )
254                {
255                    this.appendClassName(target);
256                    this.appendHashCode(target);
257                    this.appendChar('[');
258                    this.append( this.toString((Dictionary) this.target) );
259                    this.appendChar(']');
260                }
261                else if( this.target instanceof Throwable )
262                {
263                    this.append( this.toString((Throwable) this.target) );
264                }
265                else
266                {
267                    this.append( this.toString( (Object) this.target ) );
268                }
269            }
270            catch (Throwable t)
271            {
272                t.printStackTrace();
273                return "<" + t + ">";
274            }
275    
276            return this.buffer.toString();
277        }
278    
279    
280        /**
281         * Create a String representation for a Throwable.
282         *
283         * @param throwable the Throwable
284         * @return the string representation
285         */
286        protected String toString(Throwable throwable)
287        {
288            String result = null;
289    
290            if( throwable == null )
291            {
292                result = NULL_STRING;
293            }
294            else
295            {
296                result = this.getStackTrace(throwable);
297            }
298    
299            return result;
300        }
301    
302        /**
303         * Create a string representation of an object array.
304         *
305         * @param array the array to print
306         * @return the result
307         */
308        protected String toString(Object[] array)
309        {
310            StringBuffer temp = new StringBuffer();
311            ArgumentToStringBuilderImpl toStringBuilder = null;
312    
313            if( array == null )
314            {
315                return NULL_STRING;
316            }
317            else
318            {
319                temp.append(LENGTH_STRING);
320                temp.append(array.length);
321                temp.append(',');
322    
323                for( int i=0; i<array.length; i++ )
324                {
325                    temp.append('[');
326                    temp.append(i);
327                    temp.append(']');
328                    temp.append('=');
329                    toStringBuilder = new ArgumentToStringBuilderImpl(array[i],this.getMaxArgLength(),this.getMode());
330                    temp.append(toStringBuilder.toString());
331    
332                    if( i<array.length-1)
333                    {
334                        temp.append(',');
335                    }
336    
337                    if( temp.length() > this.getMaxArgLength() )
338                    {
339                        break;
340                    }
341                }
342            }
343    
344            return temp.toString();
345        }
346    
347        /**
348         * Create a string representation of a boolean[].
349         *
350         * @param array the array to print
351         * @return the result
352         */
353        protected String toString(boolean[] array)
354        {
355            StringBuffer temp = new StringBuffer();
356    
357            if( array == null )
358            {
359                return NULL_STRING;
360            }
361            else
362            {
363                temp.append(LENGTH_STRING);
364                temp.append(array.length);
365                temp.append(',');
366                temp.append(VALUE_STRING);
367    
368                for( int i=0; i<array.length; i++ )
369                {
370                    temp.append(array[i]);
371                    if( i<array.length-1)
372                    {
373                        temp.append(',');
374                    }
375    
376                    if( temp.length() > this.getMaxArgLength() )
377                    {
378                        break;
379                    }
380                }
381            }
382    
383            return temp.toString();
384        }
385    
386        /**
387         * Create a string representation of a char[].
388         *
389         * @param array the array to print
390         * @return the result
391         */
392        protected String toString(char[] array)
393        {
394            StringBuffer temp = new StringBuffer();
395    
396            if( array == null )
397            {
398                return NULL_STRING;
399            }
400            else
401            {
402                temp.append(LENGTH_STRING);
403                temp.append(array.length);
404                temp.append(',');
405                temp.append(VALUE_STRING);
406    
407                for( int i=0; i<array.length; i++ )
408                {
409                    temp.append(array[i]);
410                    if( i<array.length-1)
411                    {
412                        temp.append('.');
413                    }
414    
415                    if( temp.length() > this.getMaxArgLength() )
416                    {
417                        break;
418                    }
419                }
420            }
421    
422            return temp.toString();
423        }
424    
425        /**
426         * Create a string representation of a short[].
427         *
428         * @param array the array to print
429         * @return the result
430         */
431        protected String toString(short[] array)
432        {
433            StringBuffer temp = new StringBuffer();
434    
435            if( array == null )
436            {
437                return NULL_STRING;
438            }
439            else
440            {
441                temp.append(LENGTH_STRING);
442                temp.append(array.length);
443                temp.append(',');
444                temp.append(VALUE_STRING);
445    
446                for( int i=0; i<array.length; i++ )
447                {
448                    temp.append(array[i]);
449                    if( i<array.length-1)
450                    {
451                        temp.append(',');
452                    }
453    
454                    if( temp.length() > this.getMaxArgLength() )
455                    {
456                        break;
457                    }
458                }
459            }
460    
461            return temp.toString();
462        }
463    
464        /**
465         * Create a string representation of a int[].
466         *
467         * @param array the array to print
468         * @return the result
469         */
470        protected String toString(int[] array)
471        {
472            StringBuffer temp = new StringBuffer();
473    
474            if( array == null )
475            {
476                return NULL_STRING;
477            }
478            else
479            {
480                temp.append(LENGTH_STRING);
481                temp.append(array.length);
482                temp.append(',');
483                temp.append(VALUE_STRING);
484    
485                for( int i=0; i<array.length; i++ )
486                {
487                    temp.append(array[i]);
488                    if( i<array.length-1)
489                    {
490                        temp.append(',');
491                    }
492    
493                    if( temp.length() > this.getMaxArgLength() )
494                    {
495                        break;
496                    }
497                }
498            }
499    
500            return temp.toString();
501        }
502    
503        /**
504         * Create a string representation of a char[].
505         *
506         * @param array the array to print
507         * @return the result
508         */
509        protected String toString(long[] array)
510        {
511            StringBuffer temp = new StringBuffer();
512    
513            if( array == null )
514            {
515                return NULL_STRING;
516            }
517            else
518            {
519                temp.append(LENGTH_STRING);
520                temp.append(array.length);
521                temp.append(',');
522                temp.append(VALUE_STRING);
523    
524                for( int i=0; i<array.length; i++ )
525                {
526                    temp.append(array[i]);
527                    if( i<array.length-1)
528                    {
529                        temp.append(',');
530                    }
531    
532                    if( temp.length() > this.getMaxArgLength() )
533                    {
534                        break;
535                    }
536                }
537            }
538    
539            return temp.toString();
540        }
541    
542        /**
543         * Create a string representation of a float[].
544         *
545         * @param array the array to print
546         * @return the result
547         */
548        protected String toString(float[] array)
549        {
550            StringBuffer temp = new StringBuffer();
551    
552            if( array == null )
553            {
554                return NULL_STRING;
555            }
556            else
557            {
558                temp.append(LENGTH_STRING);
559                temp.append(array.length);
560                temp.append(',');
561                temp.append(VALUE_STRING);
562    
563                for( int i=0; i<array.length; i++ )
564                {
565                    temp.append(array[i]);
566                    if( i<array.length-1)
567                    {
568                        temp.append(',');
569                    }
570    
571                    if( temp.length() > this.getMaxArgLength() )
572                    {
573                        break;
574                    }
575                }
576            }
577    
578            return temp.toString();
579        }
580    
581        /**
582         * Create a string representation of a double[].
583         *
584         * @param array the array to print
585         * @return the result
586         */
587        protected String toString(double[] array)
588        {
589            StringBuffer temp = new StringBuffer();
590    
591            if( array == null )
592            {
593                return NULL_STRING;
594            }
595            else
596            {
597                temp.append(LENGTH_STRING);
598                temp.append(array.length);
599                temp.append(',');
600                temp.append(VALUE_STRING);
601    
602                for( int i=0; i<array.length; i++ )
603                {
604                    temp.append(array[i]);
605                    if( i<array.length-1)
606                    {
607                        temp.append(',');
608                    }
609    
610                    if( temp.length() > this.getMaxArgLength() )
611                    {
612                        break;
613                    }
614                }
615            }
616    
617            return temp.toString();
618        }
619    
620        /**
621         * Create a string representation of a String.
622         *
623         * @param string the string to print
624         */
625        protected String toString(String string)
626        {
627            StringBuffer temp = new StringBuffer();
628    
629            if( string == null )
630            {
631                return NULL_STRING;
632            }
633            else
634            {
635                temp.append(LENGTH_STRING);
636                temp.append(string.length());
637                temp.append(',');
638                temp.append(VALUE_STRING);
639                temp.append(string);
640            }
641    
642            return temp.toString();
643        }
644    
645        /**
646         * Create a string representation of a char[].
647         *
648         * @param array the array to print
649         * @return the result
650         */
651        protected String toString(byte[] array)
652        {
653            StringBuffer temp = new StringBuffer();
654    
655            if( array == null )
656            {
657                temp.append(NULL_STRING);
658            }
659            else
660            {
661                temp.append(LENGTH_STRING);
662                temp.append(array.length);
663            }
664    
665            return temp.toString();
666        }
667    
668        /**
669         * Create a string representation of a java.util.Collection.
670         *
671         * @param collection the collection to print
672         * @return the result
673         */
674        protected String toString(Collection collection)
675        {
676            int index = 0;
677            StringBuffer temp = new StringBuffer();
678            ArgumentToStringBuilderImpl toStringBuilder = null;
679    
680            if( collection == null )
681            {
682              return NULL_STRING;
683            }
684            else
685            {
686                temp.append(LENGTH_STRING);
687                temp.append(collection.size());
688                temp.append(',');
689    
690                Iterator iterator = collection.iterator();
691    
692                while (iterator.hasNext())
693                {
694                    temp.append('[');
695                    temp.append(index++);
696                    temp.append(']');
697                    temp.append('=');
698    
699                    toStringBuilder = new ArgumentToStringBuilderImpl(
700                        iterator.next(),
701                        this.getMaxArgLength(),
702                        this.getMode()
703                        );
704    
705                    temp.append(toStringBuilder.toString());
706    
707                    if( index<collection.size()-1)
708                    {
709                        temp.append(',');
710                    }
711    
712                    if( temp.length() > this.getMaxArgLength() )
713                    {
714                        break;
715                    }
716                }
717            }
718    
719            return temp.toString();
720        }
721    
722        /**
723         * Create a string representation of a Dictionary.
724         *
725         * @param dictionary the collection to print
726         * @return the result
727         */
728        protected String toString(Dictionary dictionary)
729        {
730            StringBuffer temp = new StringBuffer();
731    
732            if( dictionary == null )
733            {
734                return NULL_STRING;
735            }
736            else
737            {
738                temp.append(LENGTH_STRING);
739                temp.append(dictionary.size());
740                temp.append(',');
741                temp.append(VALUE_STRING);
742                temp.append(dictionary.toString());
743            }
744    
745            return temp.toString();
746        }
747    
748        /**
749         * Create a String representation for an arbitrary object.
750         *
751         * @param object the object
752         * @return string representation
753         */
754        protected String toString(Object object)
755        {
756            String result = null;
757            String temp = null;
758            String className = null;
759    
760            if( object == null )
761            {
762                result = NULL_STRING;
763            }
764            else
765            {
766                temp = object.toString();
767    
768                className = StringUtils.replace(
769                    object.getClass().getName(),
770                    "java.lang.", ""
771                    );
772    
773                if( temp.startsWith(className) == false )
774                {
775                    int hashCode = object.hashCode();
776                    StringBuffer tempBuffer = new StringBuffer();
777                    tempBuffer.append(className);
778                    tempBuffer.append('@');
779                    tempBuffer.append(hashCode);
780                    tempBuffer.append('[');
781                    tempBuffer.append(temp);
782                    tempBuffer.append(']');
783    
784                    result = tempBuffer.toString();
785                }
786                else
787                {
788                    result = temp;
789                }
790            }
791    
792            return result;
793        }
794    
795        /**
796         * Append the hash code.
797         * @param target the object to print
798         */
799        protected void appendHashCode(Object target)
800        {
801                if ((this.mode & INCLUDE_HASHCODE) == 0)
802                {
803                        return;
804                }
805    
806            if( this.target != null )
807            {
808                this.buffer.append('@');
809                this.buffer.append(Integer.toHexString(target.hashCode()));
810            }
811        }
812    
813        /**
814         * Append the class name.
815         * @param target the object to print
816         */
817        protected void appendClassName(Object target)
818        {
819            boolean skipClassName = true;
820    
821            if ((this.mode & INCLUDE_CLASSNAME) == 0)
822            {
823                return;
824            }
825    
826            if( this.target != null )
827            {
828                String className = target.getClass().getName();
829    
830                if( target instanceof boolean[] )
831                {
832                    this.buffer.append("boolean[]");
833                }
834                else if( target instanceof byte[] )
835                {
836                    this.buffer.append("byte[]");
837                }
838                else if( target instanceof char[] )
839                {
840                    this.buffer.append("char[]");
841                }
842                else if( target instanceof short[] )
843                {
844                    this.buffer.append("short[]");
845                }
846                else if( target instanceof int[] )
847                {
848                    this.buffer.append("int[]");
849                }
850                else if( target instanceof long[] )
851                {
852                    this.buffer.append("[ong[]");
853                }
854                else if( target instanceof float[] )
855                {
856                    this.buffer.append("float[]");
857                }
858                else if( target instanceof double[] )
859                {
860                    this.buffer.append("double[]");
861                }
862                else if( target instanceof Boolean )
863                {
864                    this.buffer.append("Boolean");
865                }
866                else if( target instanceof Character )
867                {
868                    this.buffer.append("Character");
869                }
870                else if( target instanceof Short )
871                {
872                    this.buffer.append("Short");
873                }
874                else if( target instanceof Integer )
875                {
876                    this.buffer.append("Integer");
877                }
878                else if( target instanceof Long )
879                {
880                    this.buffer.append("Long");
881                }
882                else if( target instanceof Float )
883                {
884                    this.buffer.append("Float");
885                }
886                else if( target instanceof Double )
887                {
888                    this.buffer.append("Double");
889                }
890                else if( target instanceof String )
891                {
892                    this.buffer.append("String");
893                }
894                else if( target instanceof Boolean[] )
895                {
896                    this.buffer.append("Boolean[]");
897                }
898                else if( target instanceof Character[] )
899                {
900                    this.buffer.append("Character[]");
901                }
902                else if( target instanceof Short[] )
903                {
904                    this.buffer.append("Short[]");
905                }
906                else if( target instanceof Integer[] )
907                {
908                    this.buffer.append("Integer[]");
909                }
910                else if( target instanceof Long[] )
911                {
912                    this.buffer.append("Long[]");
913                }
914                else if( target instanceof Float[] )
915                {
916                    this.buffer.append("Float[]");
917                }
918                else if( target instanceof Double[] )
919                {
920                    this.buffer.append("Double[]");
921                }
922                else if( target instanceof String[] )
923                {
924                    this.buffer.append("String[]");
925                }
926                else
927                {
928                    skipClassName = false;
929                }
930    
931                if( skipClassName == false )
932                {
933                    className = StringUtils.replace(className, "java.lang.", "");
934    
935                    if( className.endsWith(";") )
936                    {
937                        this.buffer.append(className.substring(0,className.length()-1));
938                    }
939                    else
940                    {
941                        this.buffer.append(className);
942                    }
943                }
944            }
945        }
946    
947        /**
948         * Append the hash code.
949         * @param ch the object to print
950         */
951        protected void appendChar(char ch)
952        {
953            this.buffer.append(ch);
954        }
955    
956        /**
957         * @return Returns the maxLineLength.
958         */
959        protected int getMaxArgLength()
960        {
961            return maxArgLength;
962        }
963    
964        /**
965         * <p>Gets the stack trace from a Throwable as a String.</p>
966         *
967         * @param throwable  the <code>Throwable</code> to be examined
968         * @return the stack trace as generated by the exception's
969         *  <code>printStackTrace(PrintWriter)</code> method
970         */
971        protected String getStackTrace(Throwable throwable)
972        {
973            StringWriter sw = new StringWriter();
974            PrintWriter pw = new PrintWriter( sw, true );
975            throwable.printStackTrace( pw );
976            return sw.getBuffer().toString();
977        }
978    
979        /**
980         * Append a string to the internal buffer
981         * @param source the string to append
982         */
983        protected void append(String source)
984        {
985            String formattedSource = this.format(source);
986            this.buffer.append(formattedSource);
987        }
988    
989        /**
990         * Format the buffer by replacing the whitespaces and cutting
991         * away excessive fluff.
992         *
993         * @param source the source string
994         */
995        protected String format( String source )
996        {
997            boolean isTruncated = false;
998            StringBuffer stringBuffer = new StringBuffer(source);
999    
1000            // trim the string to avoid dumping tons of data
1001    
1002            if( stringBuffer.length() > this.getMaxArgLength() )
1003            {
1004                stringBuffer.delete(this.getMaxArgLength()-1, stringBuffer.length());
1005                isTruncated = true;
1006            }
1007    
1008            // remove the line breaks and tabs for logging output and replace
1009    
1010            for( int i=0; i<stringBuffer.length(); i++ )
1011            {
1012                if( ( stringBuffer.charAt(i) == '\r' ) ||
1013                    ( stringBuffer.charAt(i) == '\n' ) ||
1014                    ( stringBuffer.charAt(i) == '\t' )  )
1015                {
1016                    stringBuffer.setCharAt(i,' ');
1017                }
1018    
1019                if( ( stringBuffer.charAt(i) == SEPERATOR ) )
1020                {
1021                    stringBuffer.setCharAt(i,' ');
1022                }
1023            }
1024    
1025            // show the user that we truncated the ouptut
1026    
1027            if( isTruncated )
1028            {
1029                if (source.endsWith("]"))
1030                {
1031                    stringBuffer.append(" ...]");
1032                }
1033                else
1034                {
1035                    stringBuffer.append(" ...");
1036                }
1037            }
1038    
1039            return stringBuffer.toString();
1040        }
1041    }