WebMacroConverter.java

/*
 * $Source$
 * $Revision$
 *
 * Copyright (C) 2006 Tim Pizey
 *
 * Part of Melati (http://melati.org), a framework for the rapid
 * development of clean, maintainable web applications.
 *
 * Melati is free software; Permission is granted to copy, distribute
 * and/or modify this software under the terms either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version,
 *
 *    or
 *
 * b) any version of the Melati Software License, as published
 *    at http://melati.org
 *
 * You should have received a copy of the GNU General Public License and
 * the Melati Software License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
 * GNU General Public License and visit http://melati.org to obtain the
 * Melati Software License.
 *
 * Feel free to contact the Developers of Melati (http://melati.org),
 * if you would like to work out a different arrangement than the options
 * outlined here.  It is our intention to allow Melati to be used by as
 * wide an audience as possible.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Contact details for copyright holder:
 *
 *     Tim Pizey <timp At paneris.org>
 */
package org.melati.template.velocity;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.oro.text.perl.Perl5Util;
import org.melati.util.MelatiBugMelatiException;

/**
 * Converter from WebMacro templates to Velocity templates.
 * 
 * Note that this does not succeed for modern WebMacro syntax 
 * which uses optional #begin in #foreach.
 * 
 * @author Tim Pizey based on work by Jason vanZyl and Tim Joyce.
 * 
 * FIXME Refactor to use java regexp.
 *
 */
public final class WebMacroConverter {

  private WebMacroConverter() {}
  /**
   * The regexes to use for substitution. The regexes come
   * in pairs. The first is the string to match, the
   * second is the substitution to make.
   */
  public static final String[] regExps = {
        // Make #if directive match the Velocity directive style.
        "#if\\s*[(]\\s*(.*\\S)\\s*[)]\\s*(#begin|{)[ \\t]?",
        "#if( $1 )",

        // Remove the WM #end #else #begin usage.
        "([ \\t])?(#end|})\\s*#else\\s*(#begin|{)([ \\t])?(\\w)",
        "#{else}$5", // avoid touching a followup word with embedded comment
        
        "[ \\t]?(#end|})\\s*#else\\s*(#begin|{)[ \\t]?",
        "#else",

        // Convert nulls.
        // This converts
        // $var != null
        // to
        // $var
        "\\s*(\\$[^\\s\\!]+)\\s*!=\\s*null\\s*",
        " $1 ",
        // This converts
        // $var == null
        // to
        // !$var
        "\\s*(\\$[^\\s\\!]+)\\s*==\\s*null\\s*",
        " !$1 ",

        // Convert WM style #foreach to Velocity directive style.
        "#foreach\\s+(\\$\\w+)\\s+in\\s+(\\$[^\\s#]+)\\s*(#begin|{)[ \\t]?",
        "#foreach( $1 in $2 )",

        // Change the "}" to #end. Have to get more
        // sophisticated here. Will assume either {}
        // and no javascript, or #begin/#end with the
        // possibility of javascript.
        "\n}", // assumes that javascript is indented, WMs not!!!
        "\n#end",

        // Convert WM style #set to Velocity directive style.
        //"#set\\s+(\\$[^\\s=]+)\\s*=\\s*(.*\\S)[ \\t]*",
        "#set\\s+(\\$[^\\s=]+)\\s*=\\s*([\\S \\t]+)",
        "#set( $1 = $2 )",
        "(##[# \\t\\w]*)\\)", // fix comments included at end of line
        ")$1",

        // Convert WM style #parse to Velocity directive style.
        "#parse\\s+([^\\s#]+)[ \\t]?",
        "#parse( $1 )",

        // parse is now deprecated for web macro
        // include as template is recommended.
        // Velocity supports only parse
        // added for melati conversion
        "#include\\s+as\\s+template\\s+([^\\s#]+)[ \\t]?",
        "#parse( $1 )",

        // Convert WM style #include to Velocity directive style.
        "#include\\s+([^\\s#]+)[ \\t]?",
        "#include( $1 )",

        // Convert WM formal reference to VL syntax.
        "\\$\\(([^\\)]+)\\)",
        "\\${$1}",
        "\\${([^}\\(]+)\\(([^}]+)}\\)", // fix encapsulated brackets: {(})
        "\\${$1($2)}",

        // Velocity currently does not permit leading underscore.
        "\\$_",
        "\\$l_",
        "\\${(_[^}]+)}", // within a formal reference
        "\\${l$1}",

  };

  /**
   * Do the conversion.
   * 
   * @param in
   * @return the InputStream with substitutions applied
   */
  public static InputStream convert(InputStream in) {
    byte[] ca;
    try {
      ca = new byte[in.available()];
      in.read(ca);
      String contents = convert(new String(ca));
      //System.err.println(contents);
      return new ByteArrayInputStream(contents.getBytes());
    } catch (IOException e) {
      throw new MelatiBugMelatiException(
              "Problem loading WebMacro template as a VelocityTemplate", e);
    }
  }
  
  public static String convert(String in) { 
      /* Regular expression tool */
      final Perl5Util perl = new Perl5Util();
      for (int i = 0; i < regExps.length; i += 2) {
        while (perl.match("/" + regExps[i] + "/", in)) {
          in = perl.substitute(
              "s/" + regExps[i] + "/" + regExps[i+1] + "/", in);
        }
      }
      return in;      
  }
}