JavaFileGenerator.java Source code

Java tutorial

Introduction

Here is the source code for JavaFileGenerator.java

Source

/* Copyright (c) 2008, Paul Cager.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * Generates boiler-plate files from templates. Only very basic
 * template processing is supplied - if we need something more
 * sophisticated I suggest we use a third-party library.
 * 
 * @author paulcager
 * @since 4.2
 */
public class JavaFileGenerator {

    /**
     * @param templateName the name of the template. E.g. 
     *        "/templates/Token.template".
     * @param options the processing options in force, such
     *        as "STATIC=yes" 
     */
    public JavaFileGenerator(String templateName, Map options) {
        this.templateName = templateName;
        this.options = options;
    }

    private final String templateName;
    private final Map options;

    private String currentLine;

    /**
     * Generate the output file.
     * @param out
     * @throws IOException
     */
    public void generate(PrintWriter out) throws IOException {
        InputStream is = getClass().getResourceAsStream(templateName);
        if (is == null)
            throw new IOException("Invalid template name: " + templateName);
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        process(in, out, false);
    }

    private String peekLine(BufferedReader in) throws IOException {
        if (currentLine == null)
            currentLine = in.readLine();

        return currentLine;
    }

    private String getLine(BufferedReader in) throws IOException {
        String line = currentLine;
        currentLine = null;

        if (line == null)
            in.readLine();

        return line;
    }

    private boolean evaluate(String condition) {
        condition = condition.trim();

        Object obj = options.get(condition);

        if (obj == null) {
            return condition.equalsIgnoreCase("true") || condition.equalsIgnoreCase("yes");
        }

        if (obj instanceof Boolean) {
            return ((Boolean) obj).booleanValue();
        } else if (obj instanceof String) {
            String string = ((String) obj).trim();
            return string.length() > 0 && !string.equalsIgnoreCase("false") && !string.equalsIgnoreCase("no");
        }

        return false;
    }

    private String substitute(String text) throws IOException {
        int startPos;

        if ((startPos = text.indexOf("${")) == -1) {
            return text;
        }

        // Find matching "}".
        int braceDepth = 1;
        int endPos = startPos + 2;

        while (endPos < text.length() && braceDepth > 0) {
            if (text.charAt(endPos) == '{')
                braceDepth++;
            else if (text.charAt(endPos) == '}')
                braceDepth--;

            endPos++;
        }

        if (braceDepth != 0)
            throw new IOException("Mismatched \"{}\" in template string: " + text);

        final String variableExpression = text.substring(startPos + 2, endPos - 1);

        // Find the end of the variable name
        String value = null;

        for (int i = 0; i < variableExpression.length(); i++) {
            char ch = variableExpression.charAt(i);

            if (ch == ':' && i < variableExpression.length() - 1 && variableExpression.charAt(i + 1) == '-') {
                value = substituteWithDefault(variableExpression.substring(0, i),
                        variableExpression.substring(i + 2));
                break;
            } else if (ch == '?') {
                value = substituteWithConditional(variableExpression.substring(0, i),
                        variableExpression.substring(i + 1));
                break;
            } else if (ch != '_' && !Character.isJavaIdentifierPart(ch)) {
                throw new IOException("Invalid variable in " + text);
            }
        }

        if (value == null) {
            value = substituteWithDefault(variableExpression, "");
        }

        return text.substring(0, startPos) + value + text.substring(endPos);
    }

    /**
     * @param substring
     * @param defaultValue
     * @return
     * @throws IOException 
     */
    private String substituteWithConditional(String variableName, String values) throws IOException {
        // Split values into true and false values.

        int pos = values.indexOf(':');
        if (pos == -1)
            throw new IOException("No ':' separator in " + values);

        if (evaluate(variableName))
            return substitute(values.substring(0, pos));
        else
            return substitute(values.substring(pos + 1));
    }

    /**
     * @param variableName
     * @param defaultValue
     * @return
     */
    private String substituteWithDefault(String variableName, String defaultValue) throws IOException {
        Object obj = options.get(variableName.trim());
        if (obj == null || obj.toString().length() == 0)
            return substitute(defaultValue);

        return obj.toString();
    }

    private void write(PrintWriter out, String text) throws IOException {
        while (text.indexOf("${") != -1) {
            text = substitute(text);
        }

        out.println(text);
    }

    private void process(BufferedReader in, PrintWriter out, boolean ignoring) throws IOException {
        //    out.println("*** process ignore=" + ignoring + " : " + peekLine(in));
        while (peekLine(in) != null) {
            if (peekLine(in).trim().startsWith("#if")) {
                String line = getLine(in).trim();
                final boolean condition = evaluate(line.substring(3).trim());

                process(in, out, ignoring || !condition);

                if (peekLine(in) != null && peekLine(in).trim().startsWith("#else")) {
                    getLine(in); // Discard the #else line
                    process(in, out, ignoring || condition);
                }

                line = getLine(in);

                if (line == null)
                    throw new IOException("Missing \"#fi\"");

                if (!line.trim().startsWith("#fi"))
                    throw new IOException("Expected \"#fi\", got: " + line);
            } else if (peekLine(in).trim().startsWith("#")) {
                break;
            } else {
                String line = getLine(in);
                if (!ignoring)
                    write(out, line);
            }
        }

        out.flush();
    }

    public static void main(String[] args) throws Exception {
        Map map = new HashMap();
        map.put("falseArg", Boolean.FALSE);
        map.put("trueArg", Boolean.TRUE);
        map.put("stringValue", "someString");

        new JavaFileGenerator(args[0], map).generate(new PrintWriter(args[1]));
    }
}