001 /* 002 * $Id: SimpleTemplateEngine.java,v 1.14 2005/05/12 09:31:48 cstein Exp $version Mar 8, 2004 2:11:00 AM $user Exp $ 003 * 004 * Copyright 2003 (C) Sam Pullara. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 package groovy.text; 035 036 import groovy.lang.Binding; 037 import groovy.lang.GroovyShell; 038 import groovy.lang.Script; 039 import groovy.lang.Writable; 040 041 import java.io.BufferedReader; 042 import java.io.IOException; 043 import java.io.PrintWriter; 044 import java.io.Reader; 045 import java.io.StringWriter; 046 import java.io.Writer; 047 import java.util.Map; 048 049 import org.codehaus.groovy.control.CompilationFailedException; 050 import org.codehaus.groovy.runtime.InvokerHelper; 051 052 053 /** 054 * This simple template engine uses JSP <% %> script and <%= %> expression syntax. It also lets you use normal groovy expressions in 055 * the template text much like the new JSP EL functionality. The variable 'out' is bound to the writer that the template is being written to. 056 * 057 * @author sam 058 */ 059 public class SimpleTemplateEngine extends TemplateEngine { 060 061 /* (non-Javadoc) 062 * @see groovy.util.TemplateEngine#createTemplate(java.io.Reader) 063 */ 064 public Template createTemplate(Reader reader) throws CompilationFailedException, IOException { 065 SimpleTemplate template = new SimpleTemplate(); 066 GroovyShell shell = new GroovyShell(); 067 String script = template.parse(reader); 068 template.script = shell.parse(script); 069 return template; 070 } 071 072 private static class SimpleTemplate implements Template { 073 074 protected Script script; 075 076 public Writable make() { 077 return make(null); 078 } 079 080 public Writable make(final Map map) { 081 return new Writable() { 082 /** 083 * Write the template document with the set binding applied to the writer. 084 * 085 * @see groovy.lang.Writable#writeTo(java.io.Writer) 086 */ 087 public Writer writeTo(Writer writer) { 088 Binding binding; 089 if (map == null) binding = new Binding(); else binding = new Binding(map); 090 Script scriptObject = InvokerHelper.createScript(script.getClass(), binding); 091 PrintWriter pw = new PrintWriter(writer); 092 scriptObject.setProperty("out", pw); 093 scriptObject.run(); 094 pw.flush(); 095 return writer; 096 } 097 098 /** 099 * Convert the template and binding into a result String. 100 * 101 * @see java.lang.Object#toString() 102 */ 103 public String toString() { 104 try { 105 StringWriter sw = new StringWriter(); 106 writeTo(sw); 107 return sw.toString(); 108 } catch (Exception e) { 109 return e.toString(); 110 } 111 } 112 }; 113 } 114 115 /** 116 * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly 117 * into the script while escaping quotes. 118 * 119 * @param reader 120 * @return 121 * @throws IOException 122 */ 123 protected String parse(Reader reader) throws IOException { 124 if (!reader.markSupported()) { 125 reader = new BufferedReader(reader); 126 } 127 StringWriter sw = new StringWriter(); 128 startScript(sw); 129 boolean start = false; 130 int c; 131 while((c = reader.read()) != -1) { 132 if (c == '<') { 133 reader.mark(1); 134 c = reader.read(); 135 if (c != '%') { 136 sw.write('<'); 137 reader.reset(); 138 } else { 139 reader.mark(1); 140 c = reader.read(); 141 if (c == '=') { 142 groovyExpression(reader, sw); 143 } else { 144 reader.reset(); 145 groovySection(reader, sw); 146 } 147 } 148 continue; // at least '<' is consumed ... read next chars. 149 } 150 if (c == '\"') { 151 sw.write('\\'); 152 } 153 sw.write(c); 154 } 155 endScript(sw); 156 String result = sw.toString(); 157 //System.out.println( "source text:\n" + result ); 158 return result; 159 } 160 161 private void startScript(StringWriter sw) { 162 sw.write("/* Generated by SimpleTemplateEngine */ "); 163 sw.write("out.print(\""); 164 } 165 166 private void endScript(StringWriter sw) { 167 sw.write("\");\n"); 168 } 169 170 /** 171 * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>. 172 * 173 * @param reader 174 * @param sw 175 * @throws IOException 176 */ 177 private void groovyExpression(Reader reader, StringWriter sw) throws IOException { 178 sw.write("\");out.print(\"${"); 179 int c; 180 while((c = reader.read()) != -1) { 181 if (c == '%') { 182 c = reader.read(); 183 if (c != '>') { 184 sw.write('%'); 185 } else { 186 break; 187 } 188 } 189 sw.write(c); 190 } 191 sw.write("}\");out.print(\""); 192 } 193 194 /** 195 * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>. 196 * 197 * @param reader 198 * @param sw 199 * @throws IOException 200 */ 201 private void groovySection(Reader reader, StringWriter sw) throws IOException { 202 sw.write("\");"); 203 int c; 204 while((c = reader.read()) != -1) { 205 if (c == '%') { 206 c = reader.read(); 207 if (c != '>') { 208 sw.write('%'); 209 } else { 210 break; 211 } 212 } 213 sw.write(c); 214 } 215 sw.write(";out.print(\""); 216 } 217 218 } 219 }