001: /*
002: * $Id: SimpleTemplateEngine.java 4032 2006-08-30 07:18:49Z mguillem $
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: * This simple template engine uses JSP <% %> script and <%= %> expression syntax. It also lets you use normal groovy expressions in
054: * 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.
055: *
056: * @author sam
057: * @author Christian Stein
058: */
059: public class SimpleTemplateEngine extends TemplateEngine {
060:
061: private final boolean verbose;
062:
063: public SimpleTemplateEngine() {
064: this (false);
065: }
066:
067: public SimpleTemplateEngine(boolean verbose) {
068: this .verbose = verbose;
069: }
070:
071: public Template createTemplate(Reader reader)
072: throws CompilationFailedException, IOException {
073: SimpleTemplate template = new SimpleTemplate();
074: GroovyShell shell = new GroovyShell();
075: String script = template.parse(reader);
076: if (verbose) {
077: System.out.println("\n-- script source --");
078: System.out.print(script);
079: System.out.println("\n-- script end --\n");
080: }
081: template.script = shell.parse(script);
082: return template;
083: }
084:
085: private static class SimpleTemplate implements Template {
086:
087: protected Script script;
088:
089: public Writable make() {
090: return make(null);
091: }
092:
093: public Writable make(final Map map) {
094: return new Writable() {
095: /**
096: * Write the template document with the set binding applied to the writer.
097: *
098: * @see groovy.lang.Writable#writeTo(java.io.Writer)
099: */
100: public Writer writeTo(Writer writer) {
101: Binding binding;
102: if (map == null)
103: binding = new Binding();
104: else
105: binding = new Binding(map);
106: Script scriptObject = InvokerHelper.createScript(
107: script.getClass(), binding);
108: PrintWriter pw = new PrintWriter(writer);
109: scriptObject.setProperty("out", pw);
110: scriptObject.run();
111: pw.flush();
112: return writer;
113: }
114:
115: /**
116: * Convert the template and binding into a result String.
117: *
118: * @see java.lang.Object#toString()
119: */
120: public String toString() {
121: try {
122: StringWriter sw = new StringWriter();
123: writeTo(sw);
124: return sw.toString();
125: } catch (Exception e) {
126: return e.toString();
127: }
128: }
129: };
130: }
131:
132: /**
133: * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly
134: * into the script while escaping quotes.
135: *
136: * @param reader
137: * @throws IOException
138: */
139: protected String parse(Reader reader) throws IOException {
140: if (!reader.markSupported()) {
141: reader = new BufferedReader(reader);
142: }
143: StringWriter sw = new StringWriter();
144: startScript(sw);
145: boolean start = false;
146: int c;
147: while ((c = reader.read()) != -1) {
148: if (c == '<') {
149: reader.mark(1);
150: c = reader.read();
151: if (c != '%') {
152: sw.write('<');
153: reader.reset();
154: } else {
155: reader.mark(1);
156: c = reader.read();
157: if (c == '=') {
158: groovyExpression(reader, sw);
159: } else {
160: reader.reset();
161: groovySection(reader, sw);
162: }
163: }
164: continue; // at least '<' is consumed ... read next chars.
165: }
166: if (c == '\"') {
167: sw.write('\\');
168: }
169: /*
170: * Handle raw new line characters.
171: */
172: if (c == '\n' || c == '\r') {
173: if (c == '\r') { // on Windows, "\r\n" is a new line.
174: reader.mark(1);
175: c = reader.read();
176: if (c != '\n') {
177: reader.reset();
178: }
179: }
180: sw.write("\\n\");\nout.print(\"");
181: continue;
182: }
183: sw.write(c);
184: }
185: endScript(sw);
186: String result = sw.toString();
187: return result;
188: }
189:
190: private void startScript(StringWriter sw) {
191: sw.write("/* Generated by SimpleTemplateEngine */\n");
192: sw.write("out.print(\"");
193: }
194:
195: private void endScript(StringWriter sw) {
196: sw.write("\");\n");
197: }
198:
199: /**
200: * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
201: *
202: * @param reader
203: * @param sw
204: * @throws IOException
205: */
206: private void groovyExpression(Reader reader, StringWriter sw)
207: throws IOException {
208: sw.write("\");out.print(\"${");
209: int c;
210: while ((c = reader.read()) != -1) {
211: if (c == '%') {
212: c = reader.read();
213: if (c != '>') {
214: sw.write('%');
215: } else {
216: break;
217: }
218: }
219: if (c != '\n' && c != '\r') {
220: sw.write(c);
221: }
222: }
223: sw.write("}\");\nout.print(\"");
224: }
225:
226: /**
227: * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
228: *
229: * @param reader
230: * @param sw
231: * @throws IOException
232: */
233: private void groovySection(Reader reader, StringWriter sw)
234: throws IOException {
235: sw.write("\");");
236: int c;
237: while ((c = reader.read()) != -1) {
238: if (c == '%') {
239: c = reader.read();
240: if (c != '>') {
241: sw.write('%');
242: } else {
243: break;
244: }
245: }
246: /* Don't eat EOL chars in sections - as they are valid instruction separators.
247: * See http://jira.codehaus.org/browse/GROOVY-980
248: */
249: // if (c != '\n' && c != '\r') {
250: sw.write(c);
251: //}
252: }
253: sw.write(";\nout.print(\"");
254: }
255:
256: }
257: }
|