View Javadoc

    1   /* $Id: GStringTemplateEngine.java,v 1.6 2004/07/27 14:23:09 tug Exp $
    2
    3   Copyright 2004 (C) John Wilson. All Rights Reserved.
    4
    5   Redistribution and use of this software and associated documentation
    6   ("Software"), with or without modification, are permitted provided
    7   that the following conditions are met:
    8
    9   1. Redistributions of source code must retain copyright
    10     statements and notices.  Redistributions must also contain a
    11     copy of this document.
    12
    13  2. Redistributions in binary form must reproduce the
    14     above copyright notice, this list of conditions and the
    15     following disclaimer in the documentation and/or other
    16     materials provided with the distribution.
    17
    18  3. The name "groovy" must not be used to endorse or promote
    19     products derived from this Software without prior written
    20     permission of The Codehaus.  For written permission,
    21     please contact info@codehaus.org.
    22
    23  4. Products derived from this Software may not be called "groovy"
    24     nor may "groovy" appear in their names without prior written
    25     permission of The Codehaus. "groovy" is a registered
    26     trademark of The Codehaus.
    27
    28  5. Due credit should be given to The Codehaus -
    29     http://groovy.codehaus.org/
    30
    31  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    32  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    33  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    34  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    35  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    36  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    37  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    38  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    39  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    40  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    41  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    42  OF THE POSSIBILITY OF SUCH DAMAGE.
    43
    44  */
    45  package groovy.text;
    46
    47  import groovy.lang.*;
    48
    49  import java.io.IOException;
    50  import java.io.PrintWriter;
    51  import java.io.Reader;
    52  import java.io.StringWriter;
    53  import java.io.Writer;
    54  import java.security.AccessController;
    55  import java.security.PrivilegedAction;
    56  import java.util.Map;
    57
    58  import org.codehaus.groovy.control.CompilationFailedException;
    59
    60
    61  /***
    62  * @author tug@wilson.co.uk
    63  *
    64  */
    65  public class GStringTemplateEngine extends TemplateEngine {
    66  	/* (non-Javadoc)
    67  	 * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
    68  	 */
    69  	public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
    70  		return new GStringTemplate(reader);
    71  	}
    72
    73  	private static class GStringTemplate implements Template {
    74  		final Closure template;
    75
    76  		/***
    77  		 * Turn the template into a writable Closure
    78  		 * When executed the closure evaluates all the code embedded in the
    79  		 * template and then writes a GString containing the fixed and variable items
    80  		 * to the writer passed as a paramater
    81  		 *
    82  		 * For example:
    83  		 *
    84  		 * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
    85  		 *
    86  		 * would compile into:
    87  		 *
    88  		 * { |out| out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
    89  		 *
    90  		 * @param reader
    91  		 * @throws CompilationFailedException
    92  		 * @throws ClassNotFoundException
    93  		 * @throws IOException
    94  		 */
    95  		public GStringTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
    96  			final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\nclass C { getTemplate() { { |out| out << \"");
    97  			boolean writingString = true;
    98
    99  	        while(true) {
    100 	        	int c = reader.read();
    101
    102 	        		if (c == -1) break;
    103
    104 	            if (c == '<') {
    105 	                c = reader.read();
    106
    107 	                if (c == '%') {
    108 		                c = reader.read();
    109
    110 		                if (c == '=') {
    111 		                		parseExpression(reader, writingString, templateExpressions);
    112 		                		writingString = true;
    113 		                		continue;
    114 		                } else {
    115 		                		parseSection(reader, writingString, templateExpressions);
    116 		                		writingString = false;
    117 		                		continue;
    118 		                }
    119 	                } else {
    120 	                	appendCharacter('<', templateExpressions, writingString);
    121 	                	writingString = true;
    122 	                }
    123 	            } else if (c == '"') {
    124 	                	appendCharacter('//', templateExpressions, writingString);
    125 	                	writingString = true;
    126            		}
    127
    128 	            	appendCharacter((char)c, templateExpressions, writingString);
    129 	            	writingString = true;
    130 	        }
    131
    132 	        if (writingString) {
    133 	        		templateExpressions.append("\"");
    134 	        }
    135
    136 	        templateExpressions.append("}.asWritable()}}");
    137
    138 //	        System.out.println(templateExpressions.toString());
    139
    140 	        final ClassLoader parentLoader = getClass().getClassLoader();
    141 	        final GroovyClassLoader loader =
    142 	        	(GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
    143 	        		public Object run() {
    144 	        			return new GroovyClassLoader(parentLoader);
    145 	        		}
    146 	        	});
    147 	        final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x"));
    148
    149 			try {
    150 				final GroovyObject object = (GroovyObject) groovyClass.newInstance();
    151
    152 				this.template = (Closure)object.invokeMethod("getTemplate", null);
    153 			} catch (InstantiationException e) {
    154 				throw new ClassNotFoundException(e.getMessage());
    155 			} catch (IllegalAccessException e) {
    156 				throw new ClassNotFoundException(e.getMessage());
    157 			}
    158 		}
    159
    160 		private static void appendCharacter(final char c,
    161 										  final StringBuffer templateExpressions,
    162 										  final boolean writingString)
    163 		{
    164 			if (!writingString) {
    165 				templateExpressions.append("out << \"");
    166 			}
    167
    168 			templateExpressions.append(c);
    169 		}
    170
    171 		/***
    172 		 * Parse a <% .... %> section
    173 		 * if we are writing a GString close and append ';'
    174 		 * then write the section as a statement
    175 		 *
    176 		 * @param reader
    177 		 * @param writingString
    178 		 * @param templateExpressions
    179 		 * @throws IOException
    180 		 */
    181 		private static void parseSection(final Reader reader,
    182 									    final boolean writingString,
    183 				                         final StringBuffer templateExpressions)
    184 			throws IOException
    185 		{
    186 			if (writingString) {
    187 				templateExpressions.append("\"; ");
    188 			}
    189
    190 	    		while (true) {
    191 	    			int c = reader.read();
    192
    193 	    			if (c == -1) break;
    194
    195 	    			if (c =='%') {
    196 	        			c = reader.read();
    197
    198 	        			if (c == '>') break;
    199 	    			}
    200
    201 	    			templateExpressions.append((char)c);
    202 	    		}
    203
    204 	    		templateExpressions.append("; ");
    205 		}
    206
    207 		/***
    208 		 * Parse a <%= .... %> expression
    209 		 *
    210 		 * @param reader
    211 		 * @param writingString
    212 		 * @param templateExpressions
    213 		 * @throws IOException
    214 		 */
    215 		private static void parseExpression(final Reader reader,
    216 										  final boolean writingString,
    217 										  final StringBuffer templateExpressions)
    218 			throws IOException
    219 		{
    220 			if (!writingString) {
    221 				templateExpressions.append("out << \"");
    222 			}
    223
    224 			templateExpressions.append("${");
    225
    226 	    		while (true) {
    227 	    			int c = reader.read();
    228
    229 	    			if (c == -1) break;
    230
    231 	    			if (c =='%') {
    232 	        			c = reader.read();
    233
    234 	        			if (c == '>') break;
    235 	    			}
    236
    237 	    			templateExpressions.append((char)c);
    238 	    		}
    239
    240 			templateExpressions.append('}');
    241 		}
    242
    243 		public Writable make() {
    244            return make(null);
    245        }
    246
    247        public Writable make(final Map map) {
    248        final Closure delegatedClosure = (Closure)this.template.clone();
    249
    250            delegatedClosure.setDelegate(new Binding(map));
    251
    252            return new Writable() {
    253                /* (non-Javadoc)
    254                * @see groovy.lang.Writable#writeTo(java.io.Writer)
    255                */
    256                public Writer writeTo(final Writer writer) throws IOException {
    257                    delegatedClosure.call(new Object[] {new PrintWriter(writer)});
    258
    259                    return writer;
    260                }
    261
    262                /* (non-Javadoc)
    263                * @see java.lang.Object#toString()
    264                */
    265                public String toString() {
    266                    final StringWriter stringWriter = new StringWriter();
    267
    268                    try {
    269                        writeTo(stringWriter);
    270
    271                        return stringWriter.toString();
    272                    } catch (final IOException e) {
    273                        return e.toString();
    274                    }
    275                }
    276
    277            };
    278        }
    279 	}
    280 }