View Javadoc

1   /* $Id: GStringTemplateEngine.java,v 1.6 2004/07/27 14:23:09 tug Exp $
2   
3   Copyright 2004 (C) John Wilson. All Rights Reserved.
4   
5   Redistribution and use of this software and associated documentation
6   ("Software"), with or without modification, are permitted provided
7   that the following conditions are met:
8   
9   1. Redistributions of source code must retain copyright
10     statements and notices.  Redistributions must also contain a
11     copy of this document.
12  
13  2. Redistributions in binary form must reproduce the
14     above copyright notice, this list of conditions and the
15     following disclaimer in the documentation and/or other
16     materials provided with the distribution.
17  
18  3. The name "groovy" must not be used to endorse or promote
19     products derived from this Software without prior written
20     permission of The Codehaus.  For written permission,
21     please contact info@codehaus.org.
22  
23  4. Products derived from this Software may not be called "groovy"
24     nor may "groovy" appear in their names without prior written
25     permission of The Codehaus. "groovy" is a registered
26     trademark of The Codehaus.
27  
28  5. Due credit should be given to The Codehaus -
29     http://groovy.codehaus.org/
30  
31  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
32  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
33  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
35  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
36  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
38  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
42  OF THE POSSIBILITY OF SUCH DAMAGE.
43  
44  */
45  package groovy.text;
46  
47  import groovy.lang.*;
48  
49  import java.io.IOException;
50  import java.io.PrintWriter;
51  import java.io.Reader;
52  import java.io.StringWriter;
53  import java.io.Writer;
54  import java.security.AccessController;
55  import java.security.PrivilegedAction;
56  import java.util.Map;
57  
58  import org.codehaus.groovy.control.CompilationFailedException;
59  
60  
61  /***
62  * @author tug@wilson.co.uk
63  *
64  */
65  public class GStringTemplateEngine extends TemplateEngine {
66  	/* (non-Javadoc)
67  	 * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
68  	 */
69  	public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
70  		return new GStringTemplate(reader);
71  	}
72  	
73  	private static class GStringTemplate implements Template {
74  		final Closure template;
75  		
76  		/***
77  		 * Turn the template into a writable Closure
78  		 * When executed the closure evaluates all the code embedded in the 
79  		 * template and then writes a GString containing the fixed and variable items 
80  		 * to the writer passed as a paramater
81  		 * 
82  		 * For example:
83  		 * 
84  		 * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
85  		 * 
86  		 * would compile into:
87  		 * 
88  		 * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
89  		 * 
90  		 * @param reader
91  		 * @throws CompilationFailedException
92  		 * @throws ClassNotFoundException
93  		 * @throws IOException
94  		 */
95  		public GStringTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
96  			final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\nclass C { getTemplate() { { |out| out << \"");
97  			boolean writingString = true;
98         
99  	        while(true) {
100 	        	int c = reader.read();
101 	        	
102 	        		if (c == -1) break;
103 	        	
104 	            if (c == '<') {
105 	                c = reader.read();
106 	                
107 	                if (c == '%') {
108 		                c = reader.read();
109 		                
110 		                if (c == '=') {
111 		                		parseExpression(reader, writingString, templateExpressions);
112 		                		writingString = true;
113 		                		continue;
114 		                } else {
115 		                		parseSection(reader, writingString, templateExpressions);
116 		                		writingString = false;
117 		                		continue;
118 		                }
119 	                } else {
120 	                	appendCharacter('<', templateExpressions, writingString);
121 	                	writingString = true;
122 	                }
123 	            } else if (c == '"') {
124 	                	appendCharacter('//', templateExpressions, writingString);
125 	                	writingString = true;
126            		}
127            		
128 	            	appendCharacter((char)c, templateExpressions, writingString);
129 	            	writingString = true;
130 	        }
131 	        
132 	        if (writingString) {
133 	        		templateExpressions.append("\"");
134 	        }
135 	        
136 	        templateExpressions.append("}.asWritable()}}");
137 	        
138 //	        System.out.println(templateExpressions.toString());
139 	        
140 	        final ClassLoader parentLoader = getClass().getClassLoader();
141 	        final GroovyClassLoader loader = 
142 	        	(GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
143 	        		public Object run() {
144 	        			return new GroovyClassLoader(parentLoader); 
145 	        		}
146 	        	});
147 	        final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x"));
148 
149 			try {
150 				final GroovyObject object = (GroovyObject) groovyClass.newInstance();
151 				
152 				this.template = (Closure)object.invokeMethod("getTemplate", null);
153 			} catch (InstantiationException e) {
154 				throw new ClassNotFoundException(e.getMessage());
155 			} catch (IllegalAccessException e) {
156 				throw new ClassNotFoundException(e.getMessage());
157 			}
158 		}
159 		
160 		private static void appendCharacter(final char c,
161 										  final StringBuffer templateExpressions,
162 										  final boolean writingString)
163 		{
164 			if (!writingString) {
165 				templateExpressions.append("out << \"");
166 			}
167 			
168 			templateExpressions.append(c);
169 		}
170 		
171 		/***
172 		 * Parse a <% .... %> section
173 		 * if we are writing a GString close and append ';'
174 		 * then write the section as a statement
175 		 * 
176 		 * @param reader
177 		 * @param writingString
178 		 * @param templateExpressions
179 		 * @throws IOException
180 		 */
181 		private static void parseSection(final Reader reader,
182 									    final boolean writingString,
183 				                         final StringBuffer templateExpressions)
184 			throws IOException
185 		{
186 			if (writingString) {
187 				templateExpressions.append("\"; ");
188 			}
189 			
190 	    		while (true) {
191 	    			int c = reader.read();
192 	    			
193 	    			if (c == -1) break;
194 	    			
195 	    			if (c =='%') {
196 	        			c = reader.read();
197 	    				
198 	        			if (c == '>') break;
199 	    			}
200 	    			
201 	    			templateExpressions.append((char)c);
202 	    		}
203 	    		
204 	    		templateExpressions.append("; ");		                		
205 		}
206 		
207 		/***
208 		 * Parse a <%= .... %> expression
209 		 * 
210 		 * @param reader
211 		 * @param writingString
212 		 * @param templateExpressions
213 		 * @throws IOException
214 		 */
215 		private static void parseExpression(final Reader reader,
216 										  final boolean writingString,
217 										  final StringBuffer templateExpressions)
218 			throws IOException
219 		{
220 			if (!writingString) {
221 				templateExpressions.append("out << \"");
222 			}
223 			
224 			templateExpressions.append("${");
225 			
226 	    		while (true) {
227 	    			int c = reader.read();
228 	    			
229 	    			if (c == -1) break;
230 	    			
231 	    			if (c =='%') {
232 	        			c = reader.read();
233 	    				
234 	        			if (c == '>') break;
235 	    			}
236 	    			
237 	    			templateExpressions.append((char)c);
238 	    		}
239 				
240 			templateExpressions.append('}');
241 		}
242 
243 		public Writable make() {
244            return make(null);
245        }
246 
247        public Writable make(final Map map) {
248        final Closure delegatedClosure = (Closure)this.template.clone();
249            
250            delegatedClosure.setDelegate(new Binding(map));
251            
252            return new Writable() {
253                /* (non-Javadoc)
254                * @see groovy.lang.Writable#writeTo(java.io.Writer)
255                */
256                public Writer writeTo(final Writer writer) throws IOException {
257                    delegatedClosure.call(new Object[] {new PrintWriter(writer)});
258 
259                    return writer;
260                }
261 
262                /* (non-Javadoc)
263                * @see java.lang.Object#toString()
264                */
265                public String toString() {
266                    final StringWriter stringWriter = new StringWriter();
267 
268                    try {
269                        writeTo(stringWriter);
270 
271                        return stringWriter.toString();
272                    } catch (final IOException e) {
273                        return e.toString();
274                    }
275                }
276 
277            };
278        }
279 	}
280 }