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 }