17ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu/**
27ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu * Copyright (c) 2004-2011 QOS.ch
388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * All rights reserved.
47ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * Permission is hereby granted, free  of charge, to any person obtaining
688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * a  copy  of this  software  and  associated  documentation files  (the
788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * "Software"), to  deal in  the Software without  restriction, including
888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * without limitation  the rights to  use, copy, modify,  merge, publish,
988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * distribute,  sublicense, and/or sell  copies of  the Software,  and to
1088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * permit persons to whom the Software  is furnished to do so, subject to
1188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * the following conditions:
127ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
1388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * The  above  copyright  notice  and  this permission  notice  shall  be
1488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * included in all copies or substantial portions of the Software.
157ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
1688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
1788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
1888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
1988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
2088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
2188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
2288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
237ba0605dc97fb81bde8311510d27b3ccba170008Ceki Gulcu *
2488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu */
2588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcupackage org.slf4j.helpers;
2688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
2788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcuimport java.text.MessageFormat;
2888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcuimport java.util.HashMap;
2988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcuimport java.util.Map;
3088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
3188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu// contributors: lizongbo: proposed special treatment of array parameter values
32370eca42fbaa403cabdd19353c4b05f756cf5677Ceki Gulcu// Joern Huxhorn: pointed out double[] omission, suggested deep array copy
3388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu/**
3488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * Formats messages according to very simple substitution rules. Substitutions
3588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * can be made 1, 2 or more arguments.
3688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
3788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
3888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * For example,
3988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
4088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <pre>
4188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * MessageFormatter.format(&quot;Hi {}.&quot;, &quot;there&quot;)
4288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * </pre>
4388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
4488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * will return the string "Hi there.".
4588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
4688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * The {} pair is called the <em>formatting anchor</em>. It serves to designate
4788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * the location where arguments need to be substituted within the message
4888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * pattern.
4988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
5088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * In case your message contains the '{' or the '}' character, you do not have
5188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * to do anything special unless the '}' character immediately follows '{'. For
5288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * example,
5388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
5488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <pre>
5588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * MessageFormatter.format(&quot;Set {1,2,3} is not equal to {}.&quot;, &quot;1,2&quot;);
5688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * </pre>
5788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
5888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * will return the string "Set {1,2,3} is not equal to 1,2.".
5988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
6088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
6188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * If for whatever reason you need to place the string "{}" in the message
6288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * without its <em>formatting anchor</em> meaning, then you need to escape the
6388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * '{' character with '\', that is the backslash character. Only the '{'
6488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * character should be escaped. There is no need to escape the '}' character.
6588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * For example,
6688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
673c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu * <pre>
683c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu * MessageFormatter.format(&quot;Set \\{} is not equal to {}.&quot;, &quot;1,2&quot;);
693c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu * </pre>
7088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
7188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * will return the string "Set {} is not equal to 1,2.".
7288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
7388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
7488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * The escaping behavior just described can be overridden by escaping the escape
7588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * character '\'. Calling
763c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu *
7788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <pre>
7888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * MessageFormatter.format(&quot;File name is C:\\\\{}.&quot;, &quot;file.zip&quot;);
7988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * </pre>
8088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
8188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * will return the string "File name is C:\file.zip".
8288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
8388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
8488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * The formatting conventions are different than those of {@link MessageFormat}
8588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * which ships with the Java platform. This is justified by the fact that
8688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}.
8788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * This local performance difference is both measurable and significant in the
889f10490a05f7344f4b3ef657e8991f5d51934e2fCeki Gulcu * larger context of the complete logging processing chain.
8988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
9088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * <p>
9188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * See also {@link #format(String, Object)},
9288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * {@link #format(String, Object, Object)} and
9388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * {@link #arrayFormat(String, Object[])} methods for more details.
9488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu *
9588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu * @author Ceki G&uuml;lc&uuml;
963c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu * @author Joern Huxhorn
9788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu */
9888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcufinal public class MessageFormatter {
9931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    static final char DELIM_START = '{';
10031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    static final char DELIM_STOP = '}';
10131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    static final String DELIM_STR = "{}";
10231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static final char ESCAPE_CHAR = '\\';
10388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
10431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
10531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Performs single argument substitution for the 'messagePattern' passed as
10631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * parameter.
10731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * <p>
10831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * For example,
10931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
11031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * <pre>
11131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * MessageFormatter.format(&quot;Hi {}.&quot;, &quot;there&quot;);
11231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * </pre>
11331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
11431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * will return the string "Hi there.".
11531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * <p>
11631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
11731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param messagePattern
11831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The message pattern which will be parsed and formatted
11931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param argument
12031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The argument to be substituted in place of the formatting anchor
12131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return The formatted message
12231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
12331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    final public static FormattingTuple format(String messagePattern, Object arg) {
12431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return arrayFormat(messagePattern, new Object[] { arg });
12531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
12688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
12731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
12831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
12931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Performs a two argument substitution for the 'messagePattern' passed as
13031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * parameter.
13131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * <p>
13231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * For example,
13331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
13431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * <pre>
13531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * MessageFormatter.format(&quot;Hi {}. My name is {}.&quot;, &quot;Alice&quot;, &quot;Bob&quot;);
13631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * </pre>
13731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
13831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * will return the string "Hi Alice. My name is Bob.".
13931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
14031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param messagePattern
14131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The message pattern which will be parsed and formatted
14231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param arg1
14331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The argument to be substituted in place of the first formatting
14431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          anchor
14531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param arg2
14631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The argument to be substituted in place of the second formatting
14731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          anchor
14831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return The formatted message
14931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
15031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) {
15131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return arrayFormat(messagePattern, new Object[] { arg1, arg2 });
1523c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu    }
153fd74fb61746d10021f4e33b0c3cd1543a89ffc0cCeki Gulcu
15431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    static final Throwable getThrowableCandidate(Object[] argArray) {
15531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (argArray == null || argArray.length == 0) {
15631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return null;
15731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
15831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu
15931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final Object lastEntry = argArray[argArray.length - 1];
16031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (lastEntry instanceof Throwable) {
16131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return (Throwable) lastEntry;
16231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
16331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        return null;
1643c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu    }
165fd74fb61746d10021f4e33b0c3cd1543a89ffc0cCeki Gulcu
16631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    /**
16731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * Same principle as the {@link #format(String, Object)} and
16831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * {@link #format(String, Object, Object)} methods except that any number of
16931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * arguments can be passed in an array.
17031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *
17131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param messagePattern
17231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          The message pattern which will be parsed and formatted
17331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @param argArray
17431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          An array of arguments to be substituted in place of formatting
17531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     *          anchors
17631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     * @return The formatted message
17731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu     */
17831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
1793c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu
18031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        Throwable throwableCandidate = getThrowableCandidate(argArray);
1813c0ab3466b6fa6e915974c72558d64c570734700Ceki Gulcu
18231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (messagePattern == null) {
18331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return new FormattingTuple(null, argArray, throwableCandidate);
18431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
185fd74fb61746d10021f4e33b0c3cd1543a89ffc0cCeki Gulcu
18631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (argArray == null) {
18731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return new FormattingTuple(messagePattern);
18831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
189fd74fb61746d10021f4e33b0c3cd1543a89ffc0cCeki Gulcu
19031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        int i = 0;
19131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        int j;
19231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // use string builder for better multicore performance
19331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
19488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
19531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        int L;
19631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (L = 0; L < argArray.length; L++) {
19788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
19831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            j = messagePattern.indexOf(DELIM_STR, i);
19988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
20031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (j == -1) {
20131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                // no more variables
20231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                if (i == 0) { // this is a simple string
20331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    return new FormattingTuple(messagePattern, argArray, throwableCandidate);
20431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                } else { // add the tail string which contains no variables and return
20531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    // the result.
20631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    sbuf.append(messagePattern.substring(i, messagePattern.length()));
20731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
20831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                }
20931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else {
21031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                if (isEscapedDelimeter(messagePattern, j)) {
21131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    if (!isDoubleEscaped(messagePattern, j)) {
21231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        L--; // DELIM_START was escaped, thus should not be incremented
21331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        sbuf.append(messagePattern.substring(i, j - 1));
21431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        sbuf.append(DELIM_START);
21531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        i = j + 1;
21631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    } else {
21731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        // The escape character preceding the delimiter start is
21831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        // itself escaped: "abc x:\\{}"
21931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        // we have to consume one backward slash
22031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        sbuf.append(messagePattern.substring(i, j - 1));
22131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
22231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                        i = j + 2;
22331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    }
22431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                } else {
22531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    // normal case
22631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    sbuf.append(messagePattern.substring(i, j));
22731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
22831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    i = j + 2;
22931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                }
23031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
23188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu        }
23231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        // append the characters following the last {} pair.
23331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(messagePattern.substring(i, messagePattern.length()));
23431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (L < argArray.length - 1) {
23531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate);
23688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu        } else {
23731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return new FormattingTuple(sbuf.toString(), argArray, null);
23888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu        }
23988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
24088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
24131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) {
24288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
24331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (delimeterStartIndex == 0) {
24431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return false;
24531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
24631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
24731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (potentialEscape == ESCAPE_CHAR) {
24831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return true;
24931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        } else {
25031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return false;
25131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
25288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
25388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
25431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) {
25531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) {
25631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return true;
25731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        } else {
25831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return false;
25931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
26088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
26188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
26231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    // special treatment of array values was suggested by 'lizongbo'
26331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) {
26431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (o == null) {
26531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append("null");
26631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            return;
26731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
26831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (!o.getClass().isArray()) {
26931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            safeObjectAppend(sbuf, o);
27031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        } else {
27131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // check for primitive array types because they
27231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // unfortunately cannot be cast to Object[]
27331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (o instanceof boolean[]) {
27431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                booleanArrayAppend(sbuf, (boolean[]) o);
27531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof byte[]) {
27631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                byteArrayAppend(sbuf, (byte[]) o);
27731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof char[]) {
27831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                charArrayAppend(sbuf, (char[]) o);
27931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof short[]) {
28031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                shortArrayAppend(sbuf, (short[]) o);
28131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof int[]) {
28231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                intArrayAppend(sbuf, (int[]) o);
28331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof long[]) {
28431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                longArrayAppend(sbuf, (long[]) o);
28531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof float[]) {
28631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                floatArrayAppend(sbuf, (float[]) o);
28731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else if (o instanceof double[]) {
28831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                doubleArrayAppend(sbuf, (double[]) o);
28931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            } else {
29031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                objectArrayAppend(sbuf, (Object[]) o, seenMap);
29131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
29231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
29388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
29488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
29531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void safeObjectAppend(StringBuilder sbuf, Object o) {
29631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        try {
29731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            String oAsString = o.toString();
29831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(oAsString);
29931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        } catch (Throwable t) {
30031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            System.err.println("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]");
30131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            t.printStackTrace();
30231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append("[FAILED toString()]");
30331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
30488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
30531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    }
30688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
30731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) {
30831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
30931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        if (!seenMap.containsKey(a)) {
31031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            seenMap.put(a, null);
31131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            final int len = a.length;
31231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            for (int i = 0; i < len; i++) {
31331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                deeplyAppendParameter(sbuf, a[i], seenMap);
31431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                if (i != len - 1)
31531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                    sbuf.append(", ");
31631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            }
31731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            // allow repeats in siblings
31831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            seenMap.remove(a);
31931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        } else {
32031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append("...");
32131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
32231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
32388c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
32488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
32531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
32631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
32731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
32831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
32931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
33031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
33131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
33231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
33331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
33488c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
33588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
33631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
33731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
33831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
33931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
34031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
34131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
34231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
34331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
34431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
34588c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
34688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
34731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void charArrayAppend(StringBuilder sbuf, char[] a) {
34831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
34931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
35031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
35131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
35231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
35331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
35431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
35531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
35688c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
35788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
35831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
35931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
36031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
36131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
36231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
36331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
36431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
36531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
36631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
36788c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
36888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
36931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void intArrayAppend(StringBuilder sbuf, int[] a) {
37031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
37131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
37231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
37331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
37431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
37531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
37631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
37731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
37888c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
37988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
38031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void longArrayAppend(StringBuilder sbuf, long[] a) {
38131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
38231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
38331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
38431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
38531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
38631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
38731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
38831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
38988c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
39088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
39131212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
39231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
39331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
39431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
39531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
39631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
39731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
39831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
39931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
40088c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
40188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu
40231212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu    private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
40331212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append('[');
40431212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        final int len = a.length;
40531212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        for (int i = 0; i < len; i++) {
40631212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            sbuf.append(a[i]);
40731212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu            if (i != len - 1)
40831212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu                sbuf.append(", ");
40931212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        }
41031212435723e2dfd5d6716d1f6a7b0e66a1e6b38Ceki Gulcu        sbuf.append(']');
41188c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu    }
41288c4c456766193e012eb890e2208473d99b91f83Ceki Gulcu}
413