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("Hi {}.", "there") 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("Set {1,2,3} is not equal to {}.", "1,2"); 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("Set \\{} is not equal to {}.", "1,2"); 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("File name is C:\\\\{}.", "file.zip"); 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ülcü 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("Hi {}.", "there"); 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("Hi {}. My name is {}.", "Alice", "Bob"); 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