home | career | drupal | java | mac | mysql | perl | scala | uml | unix

Groovy example source code file (GStringTemplateEngine.java)

This example Groovy source code file (GStringTemplateEngine.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

closure, closure, compilationfailedexception, groovyclassloader, groovyobject, gstringtemplate, gstringtemplateengine, io, ioexception, ioexception, reader, reader, security, stringbuilder, stringbuilder, util, writable

The Groovy GStringTemplateEngine.java source code

/*
 * Copyright 2003-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovy.text;

import groovy.lang.*;

import java.io.IOException;
import java.io.Reader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;

import org.codehaus.groovy.control.CompilationFailedException;

/**
 * Processes template source files substituting variables and expressions into
 * placeholders in a template source text to produce the desired output using
 * a streaming approach. This engine has equivalent functionality to the
 * {@link SimpleTemplateEngine} but creates the template using writable
 * closures making it potentially more scalable for large templates or in streaming scenarios.
 * </P>
 * The template engine uses JSP style <% %> script and <%= %> expression syntax
 * or GString style expressions. The variable '<code>out' is bound to the writer that the template
 * is being written to.
 * </p>
 * Frequently, the template source will be in a file but here is a simple
 * example providing the template as a string:
 * <pre>
 * def binding = [
 *     firstname : "Grace",
 *     lastname  : "Hopper",
 *     accepted  : true,
 *     title     : 'Groovy for COBOL programmers'
 * ]
 * def engine = new groovy.text.GStringTemplateEngine()
 * def text = '''\
 * Dear <%= firstname %> $lastname,
 *
 * We <% if (accepted) print 'are pleased' else print 'regret' %> \
 * to inform you that your paper entitled
 * '$title' was ${ accepted ? 'accepted' : 'rejected' }.
 *
 * The conference committee.
 * '''
 * def template = engine.createTemplate(text).make(binding)
 * println template.toString()
 * </pre>
 * This example uses a mix of the JSP style and GString style placeholders
 * but you can typically use just one style if you wish. Running this
 * example will produce this output:
 * <pre>
 * Dear Grace Hopper,
 *
 * We are pleased to inform you that your paper entitled
 * 'Groovy for COBOL programmers' was accepted.
 *
 * The conference committee.
 * </pre>
 * The template engine can also be used as the engine for {@link groovy.servlet.TemplateServlet} by placing the
 * following in your <code>web.xml file (plus a corresponding servlet-mapping element):
 * <pre>
 * <servlet>
 *   <servlet-name>GStringTemplate</servlet-name>
 *   <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
 *   <init-param>
 *     <param-name>template.engine</param-name>
 *     <param-value>groovy.text.GStringTemplateEngine</param-value>
 *   </init-param>
 * </servlet>
 * </pre>
 * In this case, your template source file should be HTML with the appropriate embedded placeholders.
 *
 * @author tug@wilson.co.uk
 * @author Paul King
 */
public class GStringTemplateEngine extends TemplateEngine {
    private final ClassLoader parentLoader;
    private static int counter = 1;

    public GStringTemplateEngine() {
        this(GStringTemplate.class.getClassLoader());
    }

    public GStringTemplateEngine(ClassLoader parentLoader) {
        this.parentLoader = parentLoader;
    }

    /* (non-Javadoc)
    * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
    */
    public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
        return new GStringTemplate(reader, parentLoader);
    }

    private static class GStringTemplate implements Template {
        final Closure template;

        /**
         * Turn the template into a writable Closure
         * When executed the closure evaluates all the code embedded in the
         * template and then writes a GString containing the fixed and variable items
         * to the writer passed as a parameter
         * <p/>
         * For example:
         * <p/>
         * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
         * <p/>
         * would compile into:
         * <p/>
         * { out -> out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
         *
         * @param reader
         * @param parentLoader
         * @throws CompilationFailedException
         * @throws ClassNotFoundException
         * @throws IOException
         */
        GStringTemplate(final Reader reader, final ClassLoader parentLoader) throws CompilationFailedException, ClassNotFoundException, IOException {
            final StringBuilder templateExpressions = new StringBuilder("package groovy.tmp.templates\n def getTemplate() { return { out -> delegate = new Binding(delegate); out << \"\"\"");
            boolean writingString = true;

            while (true) {
                int c = reader.read();
                if (c == -1) break;
                if (c == '<') {
                    c = reader.read();
                    if (c == '%') {
                        c = reader.read();
                        if (c == '=') {
                            parseExpression(reader, writingString, templateExpressions);
                            writingString = true;
                            continue;
                        } else {
                            parseSection(c, reader, writingString, templateExpressions);
                            writingString = false;
                            continue;
                        }
                    } else {
                        appendCharacter('<', templateExpressions, writingString);
                        writingString = true;
                    }
                } else if (c == '"') {
                    appendCharacter('\\', templateExpressions, writingString);
                    writingString = true;
                } else if (c == '$') {
                    appendCharacter('$', templateExpressions, writingString);
                    writingString = true;
                    c = reader.read();
                    if (c == '{') {
                        appendCharacter('{', templateExpressions, writingString);
                        writingString = true;
                        parseGSstring(reader, writingString, templateExpressions);
                        writingString = true;
                        continue;
                    }
                }
                appendCharacter((char) c, templateExpressions, writingString);
                writingString = true;
            }

            if (writingString) {
                templateExpressions.append("\"\"\"");
            }

            templateExpressions.append("}.asWritable()}");

            final GroovyClassLoader loader =
                    (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
                        public Object run() {
                            return new GroovyClassLoader(parentLoader);
                        }
                    });
            final Class groovyClass;
            try {
                groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "GStringTemplateScript" + counter++ + ".groovy", "x"));
            } catch (Exception e) {
                throw new GroovyRuntimeException("Failed to parse template script (your template may contain an error or be trying to use expressions not currently supported): " + e.getMessage());
            }

            try {
                final GroovyObject object = (GroovyObject) groovyClass.newInstance();

                this.template = (Closure) object.invokeMethod("getTemplate", null);
            } catch (InstantiationException e) {
                throw new ClassNotFoundException(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new ClassNotFoundException(e.getMessage());
            }
        }

        private static void appendCharacter(final char c,
                                            final StringBuilder templateExpressions,
                                            final boolean writingString) {
            if (!writingString) {
                templateExpressions.append("out << \"\"\"");
            }
            templateExpressions.append(c);
        }

        private void parseGSstring(Reader reader, boolean writingString, StringBuilder templateExpressions) throws IOException {
            if (!writingString) {
                templateExpressions.append("\"\"\"; ");
            }
            while (true) {
                int c = reader.read();
                if (c == -1) break;
                templateExpressions.append((char) c);
                if (c == '}') {
                    break;
                }
            }
        }

        /**
         * Parse a <% .... %> section
         * if we are writing a GString close and append ';'
         * then write the section as a statement
         *
         * @param pendingC
         * @param reader
         * @param writingString
         * @param templateExpressions
         * @throws IOException
         */
        private static void parseSection(final int pendingC,
                                         final Reader reader,
                                         final boolean writingString,
                                         final StringBuilder templateExpressions)
                throws IOException {
            if (writingString) {
                templateExpressions.append("\"\"\"; ");
            }
            templateExpressions.append((char) pendingC);

            while (true) {
                int c = reader.read();
                if (c == -1) break;
                if (c == '%') {
                    c = reader.read();
                    if (c == '>') break;
                    templateExpressions.append('%');
                }
                templateExpressions.append((char) c);
            }

            templateExpressions.append(";\n ");
        }

        /**
         * Parse a <%= .... %> expression
         *
         * @param reader
         * @param writingString
         * @param templateExpressions
         * @throws IOException
         */
        private static void parseExpression(final Reader reader,
                                            final boolean writingString,
                                            final StringBuilder templateExpressions)
                throws IOException {
            if (!writingString) {
                templateExpressions.append("out << \"\"\"");
            }

            templateExpressions.append("${");

            while (true) {
                int c = reader.read();
                if (c == -1) break;
                if (c == '%') {
                    c = reader.read();
                    if (c == '>') break;
                    templateExpressions.append('%');
                }
                templateExpressions.append((char) c);
            }

            templateExpressions.append('}');
        }

        public Writable make() {
            return make(null);
        }

        public Writable make(final Map map) {
            final Closure template = (Closure) this.template.clone();
            template.setDelegate(map);
            return (Writable) template;
        }
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy GStringTemplateEngine.java source code file:

new blog posts

 

Copyright 1998-2013 Alvin Alexander, alvinalexander.com
All Rights Reserved.