| /* |
| * $Id$ |
| * |
| * Copyright 2003 (C) Sam Pullara. All Rights Reserved. |
| * |
| * Redistribution and use of this software and associated documentation |
| * ("Software"), with or without modification, are permitted provided that the |
| * following conditions are met: 1. Redistributions of source code must retain |
| * copyright statements and notices. Redistributions must also contain a copy |
| * of this document. 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the distribution. 3. |
| * The name "groovy" must not be used to endorse or promote products derived |
| * from this Software without prior written permission of The Codehaus. For |
| * written permission, please contact info@codehaus.org. 4. Products derived |
| * from this Software may not be called "groovy" nor may "groovy" appear in |
| * their names without prior written permission of The Codehaus. "groovy" is a |
| * registered trademark of The Codehaus. 5. Due credit should be given to The |
| * Codehaus - http://groovy.codehaus.org/ |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY |
| * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| */ |
| package groovy.text; |
| |
| import groovy.lang.Binding; |
| import groovy.lang.GroovyShell; |
| import groovy.lang.Script; |
| import groovy.lang.Writable; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.util.Map; |
| |
| import org.codehaus.groovy.control.CompilationFailedException; |
| import org.codehaus.groovy.runtime.InvokerHelper; |
| |
| /** |
| * This simple template engine uses JSP <% %> script and <%= %> expression syntax. It also lets you use normal groovy expressions in |
| * 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. |
| * |
| * @author sam |
| * @author Christian Stein |
| */ |
| public class SimpleTemplateEngine extends TemplateEngine { |
| |
| private final boolean verbose; |
| |
| public SimpleTemplateEngine() { |
| this(false); |
| } |
| |
| public SimpleTemplateEngine(boolean verbose) { |
| this.verbose = verbose; |
| } |
| |
| public Template createTemplate(Reader reader) throws CompilationFailedException, IOException { |
| SimpleTemplate template = new SimpleTemplate(); |
| GroovyShell shell = new GroovyShell(); |
| String script = template.parse(reader); |
| if (verbose) { |
| System.out.println("\n-- script source --"); |
| System.out.print(script); |
| System.out.println("\n-- script end --\n"); |
| } |
| template.script = shell.parse(script); |
| return template; |
| } |
| |
| private static class SimpleTemplate implements Template { |
| |
| protected Script script; |
| |
| public Writable make() { |
| return make(null); |
| } |
| |
| public Writable make(final Map map) { |
| return new Writable() { |
| /** |
| * Write the template document with the set binding applied to the writer. |
| * |
| * @see groovy.lang.Writable#writeTo(java.io.Writer) |
| */ |
| public Writer writeTo(Writer writer) { |
| Binding binding; |
| if (map == null) |
| binding = new Binding(); |
| else |
| binding = new Binding(map); |
| Script scriptObject = InvokerHelper.createScript(script.getClass(), binding); |
| PrintWriter pw = new PrintWriter(writer); |
| scriptObject.setProperty("out", pw); |
| scriptObject.run(); |
| pw.flush(); |
| return writer; |
| } |
| |
| /** |
| * Convert the template and binding into a result String. |
| * |
| * @see java.lang.Object#toString() |
| */ |
| public String toString() { |
| try { |
| StringWriter sw = new StringWriter(); |
| writeTo(sw); |
| return sw.toString(); |
| } catch (Exception e) { |
| return e.toString(); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly |
| * into the script while escaping quotes. |
| * |
| * @param reader |
| * @throws IOException |
| */ |
| protected String parse(Reader reader) throws IOException { |
| if (!reader.markSupported()) { |
| reader = new BufferedReader(reader); |
| } |
| StringWriter sw = new StringWriter(); |
| startScript(sw); |
| boolean start = false; |
| int c; |
| while ((c = reader.read()) != -1) { |
| if (c == '<') { |
| reader.mark(1); |
| c = reader.read(); |
| if (c != '%') { |
| sw.write('<'); |
| reader.reset(); |
| } else { |
| reader.mark(1); |
| c = reader.read(); |
| if (c == '=') { |
| groovyExpression(reader, sw); |
| } else { |
| reader.reset(); |
| groovySection(reader, sw); |
| } |
| } |
| continue; // at least '<' is consumed ... read next chars. |
| } |
| if (c == '\"') { |
| sw.write('\\'); |
| } |
| /* |
| * Handle raw new line characters. |
| */ |
| if (c == '\n' || c == '\r') { |
| if (c == '\r') { // on Windows, "\r\n" is a new line. |
| reader.mark(1); |
| c = reader.read(); |
| if (c != '\n') { |
| reader.reset(); |
| } |
| } |
| sw.write("\\n\");\nout.print(\""); |
| continue; |
| } |
| sw.write(c); |
| } |
| endScript(sw); |
| String result = sw.toString(); |
| return result; |
| } |
| |
| private void startScript(StringWriter sw) { |
| sw.write("/* Generated by SimpleTemplateEngine */\n"); |
| sw.write("out.print(\""); |
| } |
| |
| private void endScript(StringWriter sw) { |
| sw.write("\");\n"); |
| } |
| |
| /** |
| * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>. |
| * |
| * @param reader |
| * @param sw |
| * @throws IOException |
| */ |
| private void groovyExpression(Reader reader, StringWriter sw) throws IOException { |
| sw.write("\");out.print(\"${"); |
| int c; |
| while ((c = reader.read()) != -1) { |
| if (c == '%') { |
| c = reader.read(); |
| if (c != '>') { |
| sw.write('%'); |
| } else { |
| break; |
| } |
| } |
| if (c != '\n' && c != '\r') { |
| sw.write(c); |
| } |
| } |
| sw.write("}\");\nout.print(\""); |
| } |
| |
| /** |
| * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>. |
| * |
| * @param reader |
| * @param sw |
| * @throws IOException |
| */ |
| private void groovySection(Reader reader, StringWriter sw) throws IOException { |
| sw.write("\");"); |
| int c; |
| while ((c = reader.read()) != -1) { |
| if (c == '%') { |
| c = reader.read(); |
| if (c != '>') { |
| sw.write('%'); |
| } else { |
| break; |
| } |
| } |
| /* Don't eat EOL chars in sections - as they are valid instruction separators. |
| * See http://jira.codehaus.org/browse/GROOVY-980 |
| */ |
| // if (c != '\n' && c != '\r') { |
| sw.write(c); |
| //} |
| } |
| sw.write(";\nout.print(\""); |
| } |
| |
| } |
| } |