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: * @author tug@wilson.co.uk
063: *
064: */
065: public class GStringTemplateEngine extends TemplateEngine {
066: /* (non-Javadoc)
067: * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
068: */
069: public Template createTemplate(final Reader reader)
070: throws CompilationFailedException, ClassNotFoundException,
071: IOException {
072: return new GStringTemplate(reader);
073: }
074:
075: private static class GStringTemplate implements Template {
076: final Closure template;
077:
078: /**
079: * Turn the template into a writable Closure
080: * When executed the closure evaluates all the code embedded in the
081: * template and then writes a GString containing the fixed and variable items
082: * to the writer passed as a paramater
083: *
084: * For example:
085: *
086: * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
087: *
088: * would compile into:
089: *
090: * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
091: *
092: * @param reader
093: * @throws CompilationFailedException
094: * @throws ClassNotFoundException
095: * @throws IOException
096: */
097: public GStringTemplate(final Reader reader)
098: throws CompilationFailedException,
099: ClassNotFoundException, IOException {
100: final StringBuffer templateExpressions = new StringBuffer(
101: "package groovy.tmp.templates\n def getTemplate() { return { out -> delegate = new Binding(delegate); out << \"\"\"");
102: boolean writingString = true;
103:
104: while (true) {
105: int c = reader.read();
106:
107: if (c == -1)
108: break;
109:
110: if (c == '<') {
111: c = reader.read();
112:
113: if (c == '%') {
114: c = reader.read();
115:
116: if (c == '=') {
117: parseExpression(reader, writingString,
118: templateExpressions);
119: writingString = true;
120: continue;
121: } else {
122: parseSection(c, reader, writingString,
123: templateExpressions);
124: writingString = false;
125: continue;
126: }
127: } else {
128: appendCharacter('<', templateExpressions,
129: writingString);
130: writingString = true;
131: }
132: } else if (c == '"') {
133: appendCharacter('\\', templateExpressions,
134: writingString);
135: writingString = true;
136: }
137:
138: appendCharacter((char) c, templateExpressions,
139: writingString);
140: writingString = true;
141: }
142:
143: if (writingString) {
144: templateExpressions.append("\"\"\"");
145: }
146:
147: templateExpressions.append("}.asWritable()}");
148:
149: // System.out.println(templateExpressions.toString());
150:
151: final ClassLoader parentLoader = getClass()
152: .getClassLoader();
153: final GroovyClassLoader loader = (GroovyClassLoader) AccessController
154: .doPrivileged(new PrivilegedAction() {
155: public Object run() {
156: return new GroovyClassLoader(parentLoader);
157: }
158: });
159: final Class groovyClass = loader
160: .parseClass(new GroovyCodeSource(
161: templateExpressions.toString(), "C", "x"));
162:
163: try {
164: final GroovyObject object = (GroovyObject) groovyClass
165: .newInstance();
166:
167: this .template = (Closure) object.invokeMethod(
168: "getTemplate", null);
169: } catch (InstantiationException e) {
170: throw new ClassNotFoundException(e.getMessage());
171: } catch (IllegalAccessException e) {
172: throw new ClassNotFoundException(e.getMessage());
173: }
174: }
175:
176: private static void appendCharacter(final char c,
177: final StringBuffer templateExpressions,
178: final boolean writingString) {
179: if (!writingString) {
180: templateExpressions.append("out << \"\"\"");
181: }
182:
183: templateExpressions.append(c);
184: }
185:
186: /**
187: * Parse a <% .... %> section
188: * if we are writing a GString close and append ';'
189: * then write the section as a statement
190: *
191: * @param pendingC
192: * @param reader
193: * @param writingString
194: * @param templateExpressions
195: * @throws IOException
196: */
197: private static void parseSection(final int pendingC,
198: final Reader reader, final boolean writingString,
199: final StringBuffer templateExpressions)
200: throws IOException {
201: if (writingString) {
202: templateExpressions.append("\"\"\"; ");
203: }
204: templateExpressions.append((char) pendingC);
205:
206: while (true) {
207: int c = reader.read();
208:
209: if (c == -1)
210: break;
211:
212: if (c == '%') {
213: c = reader.read();
214:
215: if (c == '>')
216: break;
217:
218: templateExpressions.append('%');
219: }
220:
221: templateExpressions.append((char) c);
222: }
223:
224: templateExpressions.append(";\n ");
225: }
226:
227: /**
228: * Parse a <%= .... %> expression
229: *
230: * @param reader
231: * @param writingString
232: * @param templateExpressions
233: * @throws IOException
234: */
235: private static void parseExpression(final Reader reader,
236: final boolean writingString,
237: final StringBuffer templateExpressions)
238: throws IOException {
239: if (!writingString) {
240: templateExpressions.append("out << \"\"\"");
241: }
242:
243: templateExpressions.append("${");
244:
245: while (true) {
246: int c = reader.read();
247:
248: if (c == -1)
249: break;
250:
251: if (c == '%') {
252: c = reader.read();
253:
254: if (c == '>')
255: break;
256:
257: templateExpressions.append('%');
258: }
259:
260: templateExpressions.append((char) c);
261: }
262:
263: templateExpressions.append('}');
264: }
265:
266: public Writable make() {
267: return make(null);
268: }
269:
270: public Writable make(final Map map) {
271: final Closure template = (Closure) this .template.clone();
272:
273: template.setDelegate(map);
274:
275: return (Writable) template;
276: }
277: }
278: }
|