View Javadoc

1   /* Copyright 2004-2007 Brian McCallister
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.skife.jdbi.v2.unstable.stringtemplate;
17  
18  import antlr.CharScanner;
19  import org.antlr.stringtemplate.StringTemplateErrorListener;
20  import org.antlr.stringtemplate.StringTemplateGroup;
21  import org.antlr.stringtemplate.StringTemplateGroupInterface;
22  import org.antlr.stringtemplate.StringTemplateGroupLoader;
23  
24  import java.io.BufferedReader;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.UnsupportedEncodingException;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  
31  /**
32   * A StringTemplateGroupLoader, appropriate for use with
33   * {@see StringTemplateStatementLocator} which loads string template groups from the
34   * classpath. It is essentially similar to the <code>CommonGroupLoader</code> in
35   * StringTemplate except that it allows mor control over the template lexing, and accepts
36   * root paths differently.
37   */
38  public class ClasspathGroupLoader implements StringTemplateGroupLoader
39  {
40      /**
41       * A great deal of this class was  taken from PathGroupLoader in StringTemplate,
42       * it makes osme bad assumptions though, so CommonGroupLoader is reimplemented here
43       */
44  
45      private String fileCharEncoding = System.getProperty("file.encoding");
46  
47      private final ConcurrentMap<String, StringTemplateGroup> groupCache;
48      private final ConcurrentMap<String, StringTemplateGroupInterface> interfaceCache;
49      private final Class<? extends CharScanner> lexerClass;
50      private final StringTemplateErrorListener errors;
51      private final String[] dirs;
52  
53      /**
54       * Create a new instance
55       *
56       * @param lexerClass CharScanner which will be used to lex the StringTemplateGroup file
57       * @param errors     Listener for errors while parsing or processing the templates
58       * @param roots      List of roots, relative to CLASSPATH root, which will be used
59       *                   to find template group files
60       */
61      public ClasspathGroupLoader(Class<? extends CharScanner> lexerClass,
62                                  StringTemplateErrorListener errors,
63                                  String... roots)
64      {
65          groupCache = new ConcurrentHashMap<String, StringTemplateGroup>();
66          interfaceCache = new ConcurrentHashMap<String, StringTemplateGroupInterface>();
67          this.lexerClass = lexerClass;
68          this.errors = errors;
69          this.dirs = roots;
70      }
71  
72      /**
73       * Create a new instance which uses the default lexer
74       *
75       * @param errors Listener for errors while parsing or processing the templates
76       * @param roots  List of roots, relative to CLASSPATH root, which will be used
77       *               to find template group files
78       */
79      public ClasspathGroupLoader(StringTemplateErrorListener errors,
80                                  String... roots)
81      {
82          this(null, errors, roots);
83      }
84  
85      /**
86       * Create a new instance which uses the default lexer and looks for template
87       * group files relative to the root of the classpath
88       *
89       * @param errors Listener for errors while parsing or processing the templates
90       */
91      public ClasspathGroupLoader(StringTemplateErrorListener errors)
92      {
93          this(errors, "/");
94      }
95  
96      /**
97       * Create a new instance which uses the default lexer and throws
98       * IllegalStateExceptions when an error occurs during template processing
99       *
100      * @param roots List of roots, relative to CLASSPATH root, which will be used
101      *              to find template group files
102      */
103     public ClasspathGroupLoader(String... roots)
104     {
105         this(new ExplodingStringTemplateErrorListener(), roots);
106     }
107 
108     /**
109      * Create a new instance which throws IllegalStateExceptions when there is an error
110      * processing templates
111      *
112      * @param lexerClass CharScanner which will be used to lex the StringTemplateGroup file
113      * @param roots      List of roots, relative to CLASSPATH root, which will be used
114      *                   to find template group files
115      */
116     public ClasspathGroupLoader(Class<? extends CharScanner> lexerClass, String... roots)
117     {
118         this(lexerClass, new ExplodingStringTemplateErrorListener(), roots);
119     }
120 
121     /**
122      * Create a new instance which uses the default lexer, looks for files
123      * relative to the root of the classpath, and throws
124      * IllegalStateExceptions when an error occurs during template processing
125      */
126     public ClasspathGroupLoader()
127     {
128         this("/");
129     }
130 
131     private BufferedReader locate(String name)
132     {
133         for (String dir : dirs) {
134             final String fileName = dir + "/" + name;
135             ClassLoader loader = Thread.currentThread().getContextClassLoader();
136             InputStream stream = loader.getResourceAsStream(fileName);
137             if (stream == null) {
138                 loader = this.getClass().getClassLoader();
139                 stream = loader.getResourceAsStream(fileName);
140             }
141             if (stream != null) {
142                 return new BufferedReader(getInputStreamReader(stream));
143             }
144         }
145         return null;
146     }
147 
148     /**
149      * Load the group called groupName from somewhere.  Return null
150      * if no group is found.
151      */
152     public StringTemplateGroup loadGroup(String groupName)
153     {
154         if (groupCache.containsKey(groupName)) {
155             return groupCache.get(groupName);
156         }
157         final BufferedReader br = locate(groupName + ".stg");
158         if (br == null) {
159             errors.error("no such group file " + groupName + ".stg", null);
160             return null;
161         }
162         final StringTemplateGroup group = new StringTemplateGroup(br, lexerClass, errors);
163         groupCache.putIfAbsent(groupName, group);
164 
165         return group;
166     }
167 
168     /**
169      * Load a group with a specified superGroup.  Groups with
170      * region definitions must know their supergroup to find templates
171      * during parsing.
172      */
173     public StringTemplateGroup loadGroup(String groupName, StringTemplateGroup superGroup)
174     {
175         final String key = new StringBuilder(groupName).append("!@#$%^&*()").append(superGroup).toString();
176         if (groupCache.containsKey(key)) {
177             return groupCache.get(key);
178         }
179         final BufferedReader br = locate(groupName + ".stg");
180         if (br == null) {
181             errors.error("no such group file " + groupName + ".stg", null);
182             return null;
183         }
184         final StringTemplateGroup group = new StringTemplateGroup(br, lexerClass, errors, superGroup);
185         groupCache.putIfAbsent(key, group);
186 
187         return group;
188     }
189 
190     /**
191      * Load the interface called interfaceName from somewhere.  Return null
192      * if no interface is found.
193      */
194     public StringTemplateGroupInterface loadInterface(String interfaceName)
195     {
196         if (interfaceCache.containsKey(interfaceName)) {
197             return interfaceCache.get(interfaceName);
198         }
199         final BufferedReader br = locate(interfaceName + ".sti");
200         if (br == null) {
201             errors.error("no such interface file " + interfaceName + ".sti", null);
202             return null;
203         }
204         final StringTemplateGroupInterface iface = new StringTemplateGroupInterface(br, errors);
205         interfaceCache.put(interfaceName, iface);
206 
207         return iface;
208     }
209 
210     private InputStreamReader getInputStreamReader(InputStream in)
211     {
212         InputStreamReader isr = null;
213         try {
214             isr = new InputStreamReader(in, fileCharEncoding);
215         }
216         catch (UnsupportedEncodingException uee) {
217             errors.error("Invalid file character encoding: " + fileCharEncoding, null);
218         }
219         return isr;
220     }
221 
222     public void setFileCharEncoding(String fileCharEncoding)
223     {
224         this.fileCharEncoding = fileCharEncoding;
225     }
226 
227     private static class ExplodingStringTemplateErrorListener implements StringTemplateErrorListener
228     {
229 
230         public void error(String msg, Throwable e)
231         {
232             throw new IllegalStateException(msg, e);
233         }
234 
235         public void warning(String msg)
236         {
237             throw new IllegalStateException(msg);
238         }
239     }
240 }