1   /* 
2    * Copyright (c) 2004-2007 QOS.ch
3    * All rights reserved.
4    * 
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   * 
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   * 
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   */
24  
25  package org.slf4j.helpers;
26  
27  
28  /**
29    * Formats messages according to very simple substitution rules. Substitutions can be
30    * made 1, 2 or more arguments.
31    * <p>
32    * For example, 
33    * <pre>MessageFormatter.format("Hi {}.", "there");</pre> will
34    * return the string "Hi there.".
35    * <p>
36    * The {} pair is called the <em>formatting anchor</em>. It serves to designate the
37    * location where arguments need to be substituted within the message pattern.
38    * <p>
39    * In the rare case where you need to place the '{' or '}' in the message pattern 
40    * itself but do not want them to be interpreted as a formatting anchors, you can
41    * espace the '{' character with '\', that is the backslash character. Only the
42    * '{' character should be escaped. There is no need to escape the '}' character.  
43    * For example, <pre>MessageFormatter.format("File name is \\{{}}.", "App folder.zip");</pre>
44    * will return the string "File name is {App folder.zip}.". 
45    * 
46    * See {@link #format(String, Object)}, {@link #format(String, Object, Object)} 
47    * and {@link #arrayFormat(String, Object[])} methods for more details.
48    *
49    * @author Ceki G&uuml;lc&uuml;
50    */
51  public class MessageFormatter {
52    static final char DELIM_START = '{';
53    static final char DELIM_STOP = '}';
54  
55    /**
56     * Performs single argument substitution for the 'messagePattern' passed as
57     * parameter.
58     * <p>
59     * For example, <pre>MessageFormatter.format("Hi {}.", "there");</pre> will
60     * return the string "Hi there.".
61     * <p>
62     * @param messagePattern The message pattern which will be parsed and formatted
63     * @param argument The argument to be substituted in place of the formatting anchor
64     * @return The formatted message
65     */
66    public static String format(String messagePattern, Object arg) {
67      return arrayFormat(messagePattern, new Object[] {arg});   
68     }
69    
70    /**
71     *
72     * Performs a two argument substitution for the 'messagePattern' passed as
73     * parameter.
74     * <p>
75     * For example, 
76     * <pre>MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");</pre> will 
77     * return the string "Hi Alice. My name is Bob.".
78     * 
79     * @param messagePattern The message pattern which will be parsed and formatted
80     * @param arg1 The argument to be substituted in place of the first formatting anchor 
81     * @param arg2 The argument to be substituted in place of the second formatting anchor 
82     * @return The formatted message
83     */
84    public static String format(String messagePattern, Object arg1, Object arg2) {
85     return arrayFormat(messagePattern, new Object[] {arg1, arg2});   
86    }
87    
88    /**
89     * Same principle as the {@link #format(String, Object)} and 
90     * {@link #format(String, Object, Object)} methods except that
91     * any number of arguments can be passed in an array.
92     * 
93     * @param messagePattern The message pattern which will be parsed and formatted
94     * @param argArray An array of arguments to be substituted in place of formatting anchors
95     * @return The formatted message
96     */
97    public static String arrayFormat(String messagePattern, Object[] argArray) {
98      if(messagePattern == null) {
99        return null;
100     }
101     int i = 0;
102     int len = messagePattern.length();
103     int j = messagePattern.indexOf(DELIM_START);
104     
105   
106     
107     StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50);
108 
109     for (int L = 0; L < argArray.length; L++) {
110       
111       char escape = 'x';
112       
113       j = messagePattern.indexOf(DELIM_START, i);
114 
115       if (j == -1 || (j+1 == len)) {
116         // no more variables
117         if (i == 0) { // this is a simple string
118           return messagePattern;
119         } else { // add the tail string which contains no variables and return the result.
120           sbuf.append(messagePattern.substring(i, messagePattern.length()));
121           return sbuf.toString();
122         }
123       } else {
124         char delimStop = messagePattern.charAt(j + 1);
125         if (j > 0) {
126           escape = messagePattern.charAt(j - 1);
127         }
128         
129         if(escape == '\\') {
130           L--; // DELIM_START was escaped, thus should not be incremented
131           sbuf.append(messagePattern.substring(i, j-1));
132           sbuf.append(DELIM_START);
133           i = j + 1;
134         } else if ((delimStop != DELIM_STOP)) {
135           // invalid DELIM_START/DELIM_STOP pair
136           sbuf.append(messagePattern.substring(i, messagePattern.length()));
137           return sbuf.toString();
138         } else {
139           // normal case
140           sbuf.append(messagePattern.substring(i, j));
141           sbuf.append(argArray[L]);
142           i = j + 2;
143         }
144       }
145     }
146     // append the characters following the second {} pair.
147     sbuf.append(messagePattern.substring(i, messagePattern.length()));
148     return sbuf.toString();
149   }
150 }