001 /* $Id: GStringTemplateEngine.java 3386 2006-01-12 10:00:23Z tug $ 002 003 Copyright 2004 (C) John Wilson. All Rights Reserved. 004 005 Redistribution and use of this software and associated documentation 006 ("Software"), with or without modification, are permitted provided 007 that the following conditions are met: 008 009 1. Redistributions of source code must retain copyright 010 statements and notices. Redistributions must also contain a 011 copy of this document. 012 013 2. Redistributions in binary form must reproduce the 014 above copyright notice, this list of conditions and the 015 following disclaimer in the documentation and/or other 016 materials provided with the distribution. 017 018 3. The name "groovy" must not be used to endorse or promote 019 products derived from this Software without prior written 020 permission of The Codehaus. For written permission, 021 please contact info@codehaus.org. 022 023 4. Products derived from this Software may not be called "groovy" 024 nor may "groovy" appear in their names without prior written 025 permission of The Codehaus. "groovy" is a registered 026 trademark of The Codehaus. 027 028 5. Due credit should be given to The Codehaus - 029 http://groovy.codehaus.org/ 030 031 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 032 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 033 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 034 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 035 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 036 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 037 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 038 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 039 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 040 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 041 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 042 OF THE POSSIBILITY OF SUCH DAMAGE. 043 044 */ 045 package groovy.text; 046 047 import groovy.lang.Closure; 048 import groovy.lang.GroovyClassLoader; 049 import groovy.lang.GroovyCodeSource; 050 import groovy.lang.GroovyObject; 051 import groovy.lang.Writable; 052 053 import java.io.IOException; 054 import java.io.Reader; 055 import java.security.AccessController; 056 import java.security.PrivilegedAction; 057 import java.util.Map; 058 059 import org.codehaus.groovy.control.CompilationFailedException; 060 061 062 /** 063 * @author tug@wilson.co.uk 064 * 065 */ 066 public class GStringTemplateEngine extends TemplateEngine { 067 /* (non-Javadoc) 068 * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader) 069 */ 070 public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException { 071 return new GStringTemplate(reader); 072 } 073 074 private static class GStringTemplate implements Template { 075 final Closure template; 076 077 /** 078 * Turn the template into a writable Closure 079 * When executed the closure evaluates all the code embedded in the 080 * template and then writes a GString containing the fixed and variable items 081 * to the writer passed as a paramater 082 * 083 * For example: 084 * 085 * '<%= "test" %> of expr and <% test = 1 %>${test} script.' 086 * 087 * would compile into: 088 * 089 * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable() 090 * 091 * @param reader 092 * @throws CompilationFailedException 093 * @throws ClassNotFoundException 094 * @throws IOException 095 */ 096 public GStringTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException { 097 final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\n def getTemplate() { return { out -> delegate = new Binding(delegate); out << \"\"\""); 098 boolean writingString = true; 099 100 while(true) { 101 int c = reader.read(); 102 103 if (c == -1) break; 104 105 if (c == '<') { 106 c = reader.read(); 107 108 if (c == '%') { 109 c = reader.read(); 110 111 if (c == '=') { 112 parseExpression(reader, writingString, templateExpressions); 113 writingString = true; 114 continue; 115 } else { 116 parseSection(c, reader, writingString, templateExpressions); 117 writingString = false; 118 continue; 119 } 120 } else { 121 appendCharacter('<', templateExpressions, writingString); 122 writingString = true; 123 } 124 } else if (c == '"') { 125 appendCharacter('\\', templateExpressions, writingString); 126 writingString = true; 127 } 128 129 appendCharacter((char)c, templateExpressions, writingString); 130 writingString = true; 131 } 132 133 if (writingString) { 134 templateExpressions.append("\"\"\""); 135 } 136 137 templateExpressions.append("}.asWritable()}"); 138 139 // System.out.println(templateExpressions.toString()); 140 141 final ClassLoader parentLoader = getClass().getClassLoader(); 142 final GroovyClassLoader loader = 143 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() { 144 public Object run() { 145 return new GroovyClassLoader(parentLoader); 146 } 147 }); 148 final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x")); 149 150 try { 151 final GroovyObject object = (GroovyObject) groovyClass.newInstance(); 152 153 this.template = (Closure)object.invokeMethod("getTemplate", null); 154 } catch (InstantiationException e) { 155 throw new ClassNotFoundException(e.getMessage()); 156 } catch (IllegalAccessException e) { 157 throw new ClassNotFoundException(e.getMessage()); 158 } 159 } 160 161 private static void appendCharacter(final char c, 162 final StringBuffer templateExpressions, 163 final boolean writingString) 164 { 165 if (!writingString) { 166 templateExpressions.append("out << \"\"\""); 167 } 168 169 templateExpressions.append(c); 170 } 171 172 /** 173 * Parse a <% .... %> section 174 * if we are writing a GString close and append ';' 175 * then write the section as a statement 176 * 177 * @param pendingC 178 * @param reader 179 * @param writingString 180 * @param templateExpressions 181 * @throws IOException 182 */ 183 private static void parseSection(final int pendingC, 184 final Reader reader, 185 final boolean writingString, 186 final StringBuffer templateExpressions) 187 throws IOException 188 { 189 if (writingString) { 190 templateExpressions.append("\"\"\"; "); 191 } 192 templateExpressions.append((char)pendingC); 193 194 while (true) { 195 int c = reader.read(); 196 197 if (c == -1) break; 198 199 if (c =='%') { 200 c = reader.read(); 201 202 if (c == '>') break; 203 204 templateExpressions.append('%'); 205 } 206 207 templateExpressions.append((char)c); 208 } 209 210 templateExpressions.append(";\n "); 211 } 212 213 /** 214 * Parse a <%= .... %> expression 215 * 216 * @param reader 217 * @param writingString 218 * @param templateExpressions 219 * @throws IOException 220 */ 221 private static void parseExpression(final Reader reader, 222 final boolean writingString, 223 final StringBuffer templateExpressions) 224 throws IOException 225 { 226 if (!writingString) { 227 templateExpressions.append("out << \"\"\""); 228 } 229 230 templateExpressions.append("${"); 231 232 while (true) { 233 int c = reader.read(); 234 235 if (c == -1) break; 236 237 if (c =='%') { 238 c = reader.read(); 239 240 if (c == '>') break; 241 242 templateExpressions.append('%'); 243 } 244 245 templateExpressions.append((char)c); 246 } 247 248 templateExpressions.append('}'); 249 } 250 251 public Writable make() { 252 return make(null); 253 } 254 255 public Writable make(final Map map) { 256 final Closure template = (Closure)this.template.clone(); 257 258 template.setDelegate(map); 259 260 return (Writable)template; 261 } 262 } 263 }