View Javadoc

1   /*
2    * $Id: TemplateParser.java 11460 2008-03-21 00:01:52Z rossmason $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util;
12  
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.regex.Matcher;
19  import java.util.regex.Pattern;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  /**
25   * <code>TemplateParser</code> is a simple string parser that will substitute
26   * tokens in a string with values supplied in a Map.
27   */
28  public final class TemplateParser
29  {
30      public static final String ANT_TEMPLATE_STYLE = "ant";
31      public static final String SQUARE_TEMPLATE_STYLE = "square";
32      public static final String CURLY_TEMPLATE_STYLE = "curly";
33  
34      /**
35       * logger used by this class
36       */
37      protected static final Log logger = LogFactory.getLog(TemplateParser.class);
38  
39      private final Pattern pattern;
40      private final int pre;
41      private final int post;
42      private final String style;
43  
44  
45      public static TemplateParser createAntStyleParser()
46      {
47          return new TemplateParser(ANT_TEMPLATE_STYLE);
48      }
49  
50      public static TemplateParser createSquareBracesStyleParser()
51      {
52          return new TemplateParser(SQUARE_TEMPLATE_STYLE);
53      }
54  
55      public static TemplateParser createCurlyBracesStyleParser()
56      {
57          return new TemplateParser(CURLY_TEMPLATE_STYLE);
58      }
59  
60      private TemplateParser(String style)
61      {
62          if (ANT_TEMPLATE_STYLE.equals(style))
63          {
64              pattern = Pattern.compile("\\$\\{[^\\}]+\\}");
65              pre = 2;
66              post = 1;
67          }
68          else if (SQUARE_TEMPLATE_STYLE.equals(style))
69          {
70              pattern = Pattern.compile("\\[[^\\]]+\\]");
71              pre = 1;
72              post = 1;
73          }
74          else if (CURLY_TEMPLATE_STYLE.equals(style))
75          {
76              pattern = Pattern.compile("\\{[^\\}]+\\}");
77              pre = 1;
78              post = 1;
79          }
80          else
81          {
82              throw new IllegalArgumentException("Unknown template style: " + style);
83          }
84          this.style = style;
85      }
86  
87      /**
88       * Matches one or more templates against a Map of key value pairs. If a value for
89       * a template is not found in the map the template is left as is in the return
90       * String
91       * 
92       * @param props the key/value pairs to match against
93       * @param template the string containing the template place holders i.e. My name
94       *            is ${name}
95       * @return the parsed String
96       */
97      public String parse(Map props, String template)
98      {
99          return parse(props, template, null);
100     }
101 
102     /**
103      * Matches one or more templates against a Map of key value pairs. If a value for
104      * a template is not found in the map the template is left as is in the return
105      * String
106      * 
107      * @param callback a callback used to resolve the property name
108      * @param template the string containing the template place holders i.e. My name
109      *            is ${name}
110      * @return the parsed String
111      */
112     public String parse(TemplateCallback callback, String template)
113     {
114         return parse(null, template, callback);
115     }
116 
117     protected String parse(Map props, String template, TemplateCallback callback)
118     {
119         String result = template;
120         Matcher m = pattern.matcher(template);
121 
122         while (m.find())
123         {
124             Object value = null;
125 
126             String match = m.group();
127             String propname = match.substring(pre, match.length() - post);
128 
129             if (callback != null)
130             {
131                 value = callback.match(propname);
132             }
133             else if (props != null)
134             {
135                 value = props.get(propname);
136             }
137 
138             if (value == null)
139             {
140                 if (logger.isDebugEnabled())
141                 {
142                     logger.debug("Value " + propname + " not found in context");
143                 }
144             }
145             else
146             {
147                 String matchRegex = escape(match);
148                 String valueString = value.toString();
149 
150                 if (valueString.indexOf('\\') != -1)
151                 {
152                     valueString = valueString.replaceAll("\\\\", "\\\\\\\\");
153                 }
154 
155                 result = result.replaceAll(matchRegex, valueString);
156             }
157         }
158         return result;
159     }
160 
161     /**
162      * Matches one or more templates against a Map of key value pairs. If a value for
163      * a template is not found in the map the template is left as is in the return
164      * String
165      * 
166      * @param props the key/value pairs to match against
167      * @param templates A List of templates
168      * @return the parsed String
169      */
170     public List parse(Map props, List templates)
171     {
172         if (templates == null)
173         {
174             return new ArrayList();
175         }
176         List list = new ArrayList(templates.size());
177         for (Iterator iterator = templates.iterator(); iterator.hasNext();)
178         {
179             list.add(parse(props, iterator.next().toString()));
180         }
181         return list;
182     }
183 
184     /**
185      * Matches one or more templates against a Map of key value pairs. If a value for
186      * a template is not found in the map the template is left as is in the return
187      * String
188      * 
189      * @param props the key/value pairs to match against
190      * @param templates A Map of templates. The values for each map entry will be
191      *            parsed
192      * @return the parsed String
193      */
194     public Map parse(final Map props, Map templates)
195     {
196         return parse(new TemplateCallback(){
197             public Object match(String token)
198             {
199                 return props.get(token);
200             }
201         }, templates);
202     }
203 
204     public Map parse(TemplateCallback callback, Map templates)
205     {
206         if (templates == null)
207         {
208             return new HashMap();
209         }
210         Map map = new HashMap(templates.size());
211         Map.Entry entry;
212         for (Iterator iterator = templates.entrySet().iterator(); iterator.hasNext();)
213         {
214             entry = (Map.Entry) iterator.next();
215             map.put(entry.getKey(), parse(callback, entry.getValue().toString()));
216         }
217         return map;
218     }
219 
220     private String escape(String string)
221     {
222         int length = string.length();
223         if (length == 0)
224         {
225             // nothing to do
226             return string;
227         }
228         else
229         {
230             StringBuffer buffer = new StringBuffer(length * 2);
231             for (int i = 0; i < length; i++)
232             {
233                 char currentCharacter = string.charAt(i);
234                 switch (currentCharacter)
235                 {
236                     case '[' :
237                     case ']' :
238                     case '{' :
239                     case '}' :
240                     case '$' :
241                         buffer.append("\\");
242                         // fall through to append original character
243                     default :
244                         buffer.append(currentCharacter);
245                 }
246             }
247             return buffer.toString();
248         }
249     }
250 
251     public String getStyle()
252     {
253         return style;
254     }
255 
256     public boolean isContainsTemplate(String value)
257     {
258         if(value==null) return false;
259 
260         Matcher m = pattern.matcher(value);
261         return m.find();
262     }
263 
264     public static interface TemplateCallback
265     {
266         Object match(String token);
267     }
268 
269 }