View Javadoc

1   /*
2    * $Id: TemplateParser.java 7963 2007-08-21 08:53:15Z dirk.olmes $
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  
33      /**
34       * logger used by this class
35       */
36      protected static final Log logger = LogFactory.getLog(TemplateParser.class);
37  
38      private final Pattern pattern;
39      private final int pre;
40      private final int post;
41      private final String style;
42  
43  
44      public static TemplateParser createAntStyleParser()
45      {
46          return new TemplateParser(ANT_TEMPLATE_STYLE);
47      }
48  
49      public static TemplateParser createSquareBracesStyleParser()
50      {
51          return new TemplateParser(SQUARE_TEMPLATE_STYLE);
52      }
53  
54      private TemplateParser(String style)
55      {
56          if (ANT_TEMPLATE_STYLE.equals(style))
57          {
58              pattern = Pattern.compile("\\$\\{[^\\}]+\\}");
59              pre = 2;
60              post = 1;
61          }
62          else if (SQUARE_TEMPLATE_STYLE.equals(style))
63          {
64              pattern = Pattern.compile("\\[[^\\]]+\\]");
65              pre = 1;
66              post = 1;
67          }
68          else
69          {
70              throw new IllegalArgumentException("Unknown template style: " + style);
71          }
72          this.style = style;
73      }
74  
75      /**
76       * Matches one or more templates against a Map of key value pairs. If a value for
77       * a template is not found in the map the template is left as is in the return
78       * String
79       * 
80       * @param props the key/value pairs to match against
81       * @param template the string containing the template place holders i.e. My name
82       *            is ${name}
83       * @return the parsed String
84       */
85      public String parse(Map props, String template)
86      {
87          return parse(props, template, null);
88      }
89  
90      /**
91       * Matches one or more templates against a Map of key value pairs. If a value for
92       * a template is not found in the map the template is left as is in the return
93       * String
94       * 
95       * @param callback a callback used to resolve the property name
96       * @param template the string containing the template place holders i.e. My name
97       *            is ${name}
98       * @return the parsed String
99       */
100     public String parse(TemplateCallback callback, String template)
101     {
102         return parse(null, template, callback);
103     }
104 
105     protected String parse(Map props, String template, TemplateCallback callback)
106     {
107         String result = template;
108         Matcher m = pattern.matcher(template);
109 
110         while (m.find())
111         {
112             Object value = null;
113 
114             String match = m.group();
115             String propname = match.substring(pre, match.length() - post);
116 
117             if (callback != null)
118             {
119                 value = callback.match(propname);
120             }
121             else if (props != null)
122             {
123                 value = props.get(propname);
124             }
125 
126             if (value == null)
127             {
128                 if (logger.isDebugEnabled())
129                 {
130                     logger.debug("Value " + propname + " not found in context");
131                 }
132             }
133             else
134             {
135                 String matchRegex = escape(match);
136                 String valueString = value.toString();
137 
138                 if (valueString.indexOf('\\') != -1)
139                 {
140                     valueString = valueString.replaceAll("\\\\", "\\\\\\\\");
141                 }
142 
143                 result = result.replaceAll(matchRegex, valueString);
144             }
145         }
146         return result;
147     }
148 
149     /**
150      * Matches one or more templates against a Map of key value pairs. If a value for
151      * a template is not found in the map the template is left as is in the return
152      * String
153      * 
154      * @param props the key/value pairs to match against
155      * @param templates A List of templates
156      * @return the parsed String
157      */
158     public List parse(Map props, List templates)
159     {
160         if (templates == null)
161         {
162             return new ArrayList();
163         }
164         List list = new ArrayList(templates.size());
165         for (Iterator iterator = templates.iterator(); iterator.hasNext();)
166         {
167             list.add(parse(props, iterator.next().toString()));
168         }
169         return list;
170     }
171 
172     /**
173      * Matches one or more templates against a Map of key value pairs. If a value for
174      * a template is not found in the map the template is left as is in the return
175      * String
176      * 
177      * @param props the key/value pairs to match against
178      * @param templates A Map of templates. The values for each map entry will be
179      *            parsed
180      * @return the parsed String
181      */
182     public Map parse(Map props, Map templates)
183     {
184         if (templates == null)
185         {
186             return new HashMap();
187         }
188         Map map = new HashMap(templates.size());
189         Map.Entry entry;
190         for (Iterator iterator = templates.entrySet().iterator(); iterator.hasNext();)
191         {
192             entry = (Map.Entry) iterator.next();
193             map.put(entry.getKey(), parse(props, entry.getValue().toString()));
194         }
195         return map;
196     }
197 
198     private String escape(String string)
199     {
200         int length = string.length();
201         if (length == 0)
202         {
203             // nothing to do
204             return string;
205         }
206         else
207         {
208             StringBuffer buffer = new StringBuffer(length * 2);
209             for (int i = 0; i < length; i++)
210             {
211                 char currentCharacter = string.charAt(i);
212                 switch (currentCharacter)
213                 {
214                     case '[' :
215                     case ']' :
216                     case '{' :
217                     case '}' :
218                     case '$' :
219                         buffer.append("\\");
220                         // fall through to append original character
221                     default :
222                         buffer.append(currentCharacter);
223                 }
224             }
225             return buffer.toString();
226         }
227     }
228 
229     public String getStyle()
230     {
231         return style;
232     }
233 
234     public boolean isContainsTemplate(String value)
235     {
236         Matcher m = pattern.matcher(value);
237         return m.find();
238     }
239 
240     public static interface TemplateCallback
241     {
242         Object match(String token);
243     }
244 
245 }