|       /* 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]));
 }
 }
 
 
 
 
 
 
 
 
 |