Below is a (long) proposal for macro capabilities for Velocity.  I have
attached code that works with the current CVS tree.  Please give a read
through, ask questions if interested and try it if you have the time. I
am looking for list approval to get this integrated into Velocity.

Introduction
------------
Recently, during the  discussion extensions to the  Velocity template
language, the subject of adding a macro capabilities to the language was
introduced, and  a few solutions were offered :

1) Reference-accessed toolsets :   $toolset.foo( $i )  : This involves
using a set of objects presumably written in Java provided for the
designer that do certain things.  In the case of the problem that
started the discussion ($!email), then $toolset.isnull( $email ) would
result in $email being output into the output stream if it was a valid
reference in the context, or an empty string if not.

2) Velocimacros : another approach ( offered by me), recognizes that in
this case, $!email, the solution can be represented entirely w/in
Velocity template language (hereafter VTL) :

#if( $email )
     $email
#end

Further, when exploring this subject in much animated detail on the
list, it grew apparent, at least to me, that there is a need for
allowing a VTL macro expansion facility to allow those people
responsible for the View layer of the M-V-C design  pattern to
encapsulate VTL snippets into named entities and use them within their
templates.

Proposed Solution
================================

I modifed the current Velocity engine so that  it handles Velocimacros
(VM hereafter) and the code is included in this post.  There are
instructions for installation  below. (It is very simple...) Kudos to
Jason and the rest of you for such an easy environment to integrate this
into.

During the following, I will try to illustrate how the $!email problem
was solved in VM's.

The way it works is very straightforward (any C programmer will
recognize it as nothing more than a #define) :

1) There is a mechanism by which VM's are defined.  Currently, in the
example I hacked together last night, they are in the source itself (I
didn't feel the need to show I can read a file... discussion about this 
follows in the appendix). Note that the examples are not coded in Java :
they are macro definitions that get expanded and processed at parse
time.

Currently, the VM definition that solves the $!email problem looks like
:

#isnull( x ) := #if( x ) x #end

This is the definition exactly as it appears internally.  

2) They are used in the .vm template exactly like a directive :

<HTML>
<BODY>
Your email address is : #isnull $email
</BODY>
</HTML>

(I can't wait for #isnull($email)   :)

At template parse time,  once they are identified as VM's,  they are
expanded and then run back through the VTL parsing engine, and then
injected into the output stream.  No major magic.  It's very analagous
to the 'real' directives.

That's it.  

Discussion
==========

There as several important points, in no apparent order :

1) This doesn't break MVC in that anything that is in the VM is VTL code
that the  designer could have simply typed into a template, or plopped
in via cut and  paste, which I think is worse.  

2) There are no new API's offered, not even the capability of the
designer inventing API's.  The VelocimacroFactory / Proxy objects, which
are used to implement the VM  functionality,  don't do anything but
expand and parse macros.  There is no real app logic built in, other
than rendering VTL output. No db access capability, no file access,
nada.  Note that a purist might argue that #isnull $foo is a function or
subroutine, and therefore they are creating API's.  Ok, you win.   But
this is no different than using an include with agreement on references
between the includer and includee, so to speak, which is horrible : 

#include isnull.wm

which is something that designers can do now.  Using #isnull $email is
much 
easier to read as well.

3) This supports the philosophy that View (of M-V-C pattern) and View
only is responsible for rendering the output layer.  There is no way for
View to break out of the box that View is placed in (namely, only can
use HTML and VTL directives).  All this does is provide a
code-encapsulation facility to View, something we all hopefully agree is
a Good Thing. 

I have tons to say about this, as I have been pretty focused on it.  I
hope that this little doc provided enough use-case scenarios to convince
that this would be a valuable thing to add to Velocity.  

So, I am looking for votes on including the code I offer here into the
main tree. I have talked with Jason about how it integrates, so the
modification of Parser is understood to require pushing the mods
upstream to the jjt file, so that won't be an issue.

geir



Code and Appendices
===================

Try it!
-------

The code that demonstrates working VM's is simple.  There are 4 files +
1 .vm example.  They are attached to this msg if I remember.

There are two files that are framework files.  I had to make slight
modifications, and  my integration isn't done.  But they work and are
good for an example.

1) These are rife with tabs.  I also run with scissors.
2) my changes in existing code should be bracketed by '$$$ gmj' so look
for that.
3) This works with latest CVS tree.

To use :

Backup your copy  and replace with my copy :
1)
jakarta-velocity/src/java/org/apache/velocity/runtime/parser/Parser.java
2)
jakarta-velocity/src/java/org/apache/velocity/runtime/parser/ASTDirective.java

Add my new files :
1)
jakarta-velocity/src/java/org/apache/velocity/runtime/directive/VelocimacroProxy.java
2)
jakarta-velocity/src/java/org/apache/velocity/runtime/parser/VelocimacroFactory.java
                                                        ^^^^^^^^
not sure if it belongs there, but that's where it is now /

Build. (I used build/build-velocity.sh)

Test : use the velocimacro.vm template...  test.sh velocimacro.vm

To add your own velocimacro :

For now, add into the constructor of VelocimacroFactory with an
addMacro() call.  Note that you have to be nice to my cheesy parser : 
spaces are necessary.  There should be notes in the code.

Sorry that you have to rebuild as the VM definitions are in the code,
but that was just for the demo hack. Yes, this will be taken out of the
class!  I am working on that right after sending this to the list...


Where are These Defined and Other Use Cases
-------------------------------------------

These defintions will come from 3 places :

a) A global library provided with the distribution.  It can contain
#isnull and 
other common, useful VM's that are more utility that 'visual'.

b) A local library defined by the designers.  An example of this would
be an uber-designer specifying small formatting and layout macros,
things that are  too small to be #include, but repeated so you don't
want to type them.

example : suppose you are building an online store, and you have the
following bit of HTML to have a 'add to shopping cart' button for an
item :

<form action=ShoppingCartCtrl>
<input type=image name=submit src='images/incart.gif' border=0>
<input type=hidden name=what value='additem'>
<input type=hidden name=catalog_num value='1002'>
</form>

That can be (and should be) wrapped in a VM :

#addtocart( x y ) := <form action=ShoppingCartCtrl>
            <input type=image name=submit src= x  border=0>
            <input type=hidden name=what value='additem'><
            input type=hidden name=catalog_num value= y >
            </form>

(I put \n in there above for readability in this doc...)

and in your template :

#addtocart $cartpict $catalognum

(Kinda contrived, but you may want to change the image based on the
item... or something...)

c) and locally, w/in the template itself, to allow designers to also get
the benefit of not cutting and pasting the same HTML/VM snippet around

--

Control of the capabilities can be maintained via properties, allowing
each of the modes to be turned on and off, depending upon local rules. 
The three modes are not exclusive, modulo namespace clashes.  Choice is
good.

For each case a, b, c, the format of the definition would be the same :

#defineVM "#isnull( x ) := #if( x ) x #end"

Actually, a better version  of isnull....:

#defineVM "#isnull( x y ) := #if( x ) x #else y #end"



-- 
Geir Magnusson Jr.                                    
[EMAIL PROTECTED]
"Congress shall make no law abridging ...  the freedom of sXXXch, ... or
the
right of the people peaceably to XXXemble, and to peXXXion the
government for 
a redress of grievances." -even the Founding Fathers needed content
filters
/* Generated By:JJTree: Do not edit this line. ASTDirective.java */

package org.apache.velocity.runtime.parser;

import java.io.Writer;
import java.io.IOException;

import org.apache.velocity.Context;

public class ASTDirective extends SimpleNode
{
    public ASTDirective(int id)
    {
        super(id);
    }

    public ASTDirective(Parser p, int id)
    {
        super(p, id);
    }


    /** Accept the visitor. **/
    public Object jjtAccept(ParserVisitor visitor, Object data)
    {
        return visitor.visit(this, data);
    }

    public void render(Context context, Writer writer)
        throws IOException
    {
        String directive = getFirstToken().image.substring(1);

        /*
         *  $$$ gmj start
         *
         *  was
         *
         *  if (parser.isDirective(directive))
         *       parser.getDirective(directive).render(context, writer, this);
         *  else 
         *     writer.write(getFirstToken().image);
         */

        if (parser.isDirective(directive)) {
            parser.getDirective(directive).render(context, writer, this);
        }
        else if ( parser.isVelocimacro( directive ) ) {
            parser.getVelocimacro( directive ).render( context, writer, this );
        }
        else
          writer.write(getFirstToken().image);

        /*
         *   $$$ gmj end
         */
    }
}
/* Generated By:JJTree&JavaCC: Do not edit this line. Parser.java */
package org.apache.velocity.runtime.parser;

import java.io.*;
import java.util.*;

import org.apache.velocity.runtime.directive.Directive;

/**
 * This class is responsible for parsing a Velocity
 * template. This class was generated by JavaCC using
 * the JJTree extension to produce an Abstract
 * Syntax Tree (AST) of the template.
 *
 * Please look at the Parser.jjt file which is
 * what controls the generation of this class.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
 * @version $Id: Parser.java,v 1.2 2000/09/30 22:58:14 jvanzyl Exp $ 
*/
public class Parser/*@bgen(jjtree)*/implements ParserTreeConstants, ParserConstants 
{/*@bgen(jjtree)*/
  protected JJTParserState jjtree = new JJTParserState();

    Hashtable directives;


    /* This was added to allow the parser to be associated
     * with a particular syntax. JavaCC doesn't generate
     * a constructor without parameters. The normal constructor
     * takes a single argument which an InputStream. But in
     * order to make the parser dynamically loadable this
     * constructor had to be added. This also allows us to
     * create a single instance of a parser and reuse
     * it over and over.
     */
    public Parser()
    {
        this(new ByteArrayInputStream("\n".getBytes()));
    }

    /* This was also added to allow parsers to be dynamically
     * loadable.
     * 
     * Taken from the generated constructor in Parser.java.
     * Just be watchful when you change the grammar because
     * the generated method changes when the grammar changes
     * WRT to adding new token types. So you have to
     * occasionally do some cutting and pasting :-)
     *
     * It would be A LOT better it you could subclass grammars
     * and override particular methods but that's not
     * possible with JavaCC. I believe that you can do
     * this with ANTLR though.
     */
    public SimpleNode parse(InputStream stream) throws ParseException
    {
        ReInit(stream);
        return process();
    }

    public void setDirectives(Hashtable directives)
    {
        this.directives = directives;
    }

    public Directive getDirective(String directive)
    {
        return (Directive) directives.get(directive);
    }

    public boolean isDirective(String directive)
    {
        if (directives.containsKey(directive))
            return true;
        else
            return false;
    }

    /*
     *  $$$ gmj start
     */

    VelocimacroFactory vmFactory_ = new VelocimacroFactory();

    public boolean isVelocimacro( String vm )
    {
        return vmFactory_.isVelocimacro( vm );
    }

    public Directive getVelocimacro( String vm )
    {
        return vmFactory_.get( vm );
    }
    
    /*
     * $$$ gmj end
     */

/**
 * This method is what starts the whole parsing
 * process. After the parsing is complete and
 * the template has been turned into an AST,
 * this method returns the root of AST which
 * can subsequently be traversed by a visitor
 * which implements the ParserVisitor interface
 * which is generated automatically by JavaCC
 */
  final public SimpleNode process() throws ParseException {
                        /*@bgen(jjtree) process */
  ASTprocess jjtn000 = new ASTprocess(this, JJTPROCESS);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      label_1:
      while (true) {
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case ESCAPE_SEQUENCE:
        case TEXT:
        case SINGLE_LINE_COMMENT:
        case FORMAL_COMMENT:
        case MULTI_LINE_COMMENT:
        case INCLUDE_DIRECTIVE:
        case IF_DIRECTIVE:
        case STOP_DIRECTIVE:
        case NUMBER_LITERAL:
        case WORD:
        case IDENTIFIER:
        case DOT:
        case SHORTHAND:
        case LCURLY:
          ;
          break;
        default:
          jj_la1[0] = jj_gen;
          break label_1;
        }
        Statement();
      }
      jj_consume_token(0);
     jjtree.closeNodeScope(jjtn000, true);
     jjtc000 = false;
     {if (true) return jjtn000;}
    } catch (Throwable jjte000) {
     if (jjtc000) {
       jjtree.clearNodeScope(jjtn000);
       jjtc000 = false;
     } else {
       jjtree.popNode();
     }
     if (jjte000 instanceof RuntimeException) {
       {if (true) throw (RuntimeException)jjte000;}
     }
     if (jjte000 instanceof ParseException) {
       {if (true) throw (ParseException)jjte000;}
     }
     {if (true) throw (Error)jjte000;}
    } finally {
     if (jjtc000) {
       jjtree.closeNodeScope(jjtn000, true);
     }
    }
    throw new Error("Missing return statement in function");
  }

// -----------------------------------------------------------------------
// 
// Statement Syntax
// 
// -----------------------------------------------------------------------

/**
 * These are the types of statements that
 * are acceptable in Velocity templates.
 * I have not found that the order here
 * matters much. Someone please correct
 * me here if I'm wrong.
 */
  final public void Statement() throws ParseException {
    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
    case ESCAPE_SEQUENCE:
    case TEXT:
    case NUMBER_LITERAL:
    case DOT:
      Text();
      break;
    case IF_DIRECTIVE:
      IfStatement();
      break;
    case INCLUDE_DIRECTIVE:
      IncludeStatement();
      break;
    case STOP_DIRECTIVE:
      StopStatement();
      break;
    case IDENTIFIER:
    case SHORTHAND:
    case LCURLY:
      Reference();
      break;
    case SINGLE_LINE_COMMENT:
    case FORMAL_COMMENT:
    case MULTI_LINE_COMMENT:
      Comment();
      break;
    case WORD:
      Directive();
      break;
    default:
      jj_la1[1] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
  }

  final public void Comment() throws ParseException {
                  /*@bgen(jjtree) Comment */
  ASTComment jjtn000 = new ASTComment(this, JJTCOMMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case SINGLE_LINE_COMMENT:
        jj_consume_token(SINGLE_LINE_COMMENT);
        break;
      case MULTI_LINE_COMMENT:
        jj_consume_token(MULTI_LINE_COMMENT);
        break;
      case FORMAL_COMMENT:
        jj_consume_token(FORMAL_COMMENT);
        break;
      default:
        jj_la1[2] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void NumberLiteral() throws ParseException {
                        /*@bgen(jjtree) NumberLiteral */
  ASTNumberLiteral jjtn000 = new ASTNumberLiteral(this, JJTNUMBERLITERAL);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(NUMBER_LITERAL);
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void StringLiteral() throws ParseException {
                        /*@bgen(jjtree) StringLiteral */
  ASTStringLiteral jjtn000 = new ASTStringLiteral(this, JJTSTRINGLITERAL);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(STRING_LITERAL);
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

/**
 * This method corresponds to variable
 * references in Velocity templates.
 * The following are examples of variable
 * references that may be found in a
 * template:
 *
 * $foo
 * $bar
 *
 */
  final public void Identifier() throws ParseException {
                     /*@bgen(jjtree) Identifier */
  ASTIdentifier jjtn000 = new ASTIdentifier(this, JJTIDENTIFIER);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(IDENTIFIER);
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  void DirectiveArgs(int args) throws ParseException {
 /*@bgen(jjtree) DirectiveArgs */
 ASTDirectiveArgs jjtn000 = new ASTDirectiveArgs(this, JJTDIRECTIVEARGS);
 boolean jjtc000 = true;
 jjtree.openNodeScope(jjtn000);
 try {for (int i = 0; i < args; i++)
    {
        DirectiveArg();
    }/*@bgen(jjtree)*/
 } finally {
   if (jjtc000) {
     jjtree.closeNodeScope(jjtn000, true);
   }
 }
  }

  final public void DirectiveArg() throws ParseException {
    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
    case LPAREN:
    case LBRACKET:
    case STRING_LITERAL:
    case TRUE:
    case FALSE:
    case LOGICAL_NOT:
    case NUMBER_LITERAL:
    case IDENTIFIER:
    case SHORTHAND:
    case LCURLY:
      Expression();
      break;
    case WORD:
      jj_consume_token(WORD);
      break;
    default:
      jj_la1[3] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
  }

  final public SimpleNode Directive() throws ParseException {
 /*@bgen(jjtree) Directive */
    ASTDirective jjtn000 = new ASTDirective(this, JJTDIRECTIVE);
    boolean jjtc000 = true;
    jjtree.openNodeScope(jjtn000);Token t;
    int args;
    Directive d;
    try {
      t = jj_consume_token(WORD);

        d = (Directive) directives.get(t.image.substring(1));

        if (d == null)
        {
            /*
             *  $$$ gmj start
             * 
             *  was
             *     token_source.SwitchTo(DEFAULT);
             *     {if (true) return jjtn000;}
             *
             *  if null, then not a real directive, but maybe a velocimacro
             */

            d  = (Directive) vmFactory_.get( t.image.substring(1) );

            if (d == null) {
                token_source.SwitchTo( DEFAULT );
                return jjtn000;
            }

            /*
             *   $$$ gmj end
             */
        }
        
        args = d.getArgs();

        DirectiveArgs(args);

        if (d.getType() == Directive.LINE)
        {

            {if (true) return jjtn000;}
        }
        token_source.SwitchTo(DEFAULT);
      ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
      boolean jjtc001 = true;
      jjtree.openNodeScope(jjtn001);
      try {
        label_2:
        while (true) {
          Statement();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case ESCAPE_SEQUENCE:
          case TEXT:
          case SINGLE_LINE_COMMENT:
          case FORMAL_COMMENT:
          case MULTI_LINE_COMMENT:
          case INCLUDE_DIRECTIVE:
          case IF_DIRECTIVE:
          case STOP_DIRECTIVE:
          case NUMBER_LITERAL:
          case WORD:
          case IDENTIFIER:
          case DOT:
          case SHORTHAND:
          case LCURLY:
            ;
            break;
          default:
            jj_la1[4] = jj_gen;
            break label_2;
          }
        }
      } catch (Throwable jjte001) {
      if (jjtc001) {
        jjtree.clearNodeScope(jjtn001);
        jjtc001 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte001 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte001;}
      }
      if (jjte001 instanceof ParseException) {
        {if (true) throw (ParseException)jjte001;}
      }
      {if (true) throw (Error)jjte001;}
      } finally {
      if (jjtc001) {
        jjtree.closeNodeScope(jjtn001, true);
      }
      }
      jj_consume_token(END);
      jjtree.closeNodeScope(jjtn000, true);
      jjtc000 = false;
        {if (true) return jjtn000;}
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
    throw new Error("Missing return statement in function");
  }

  final public void ObjectArray() throws ParseException {
                      /*@bgen(jjtree) ObjectArray */
  ASTObjectArray jjtn000 = new ASTObjectArray(this, JJTOBJECTARRAY);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(LBRACKET);
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LBRACKET:
      case STRING_LITERAL:
      case TRUE:
      case FALSE:
      case IDENTIFIER:
      case SHORTHAND:
      case LCURLY:
        Parameter();
        label_3:
        while (true) {
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case COMMA:
            ;
            break;
          default:
            jj_la1[5] = jj_gen;
            break label_3;
          }
          jj_consume_token(COMMA);
          Parameter();
        }
        break;
      default:
        jj_la1[6] = jj_gen;
        ;
      }
      jj_consume_token(RBRACKET);
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

/**
 * This method has yet to be fully implemented
 * but will allow arbitrarily nested method
 * calls
 */
  final public void Parameter() throws ParseException {
    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
    case STRING_LITERAL:
      StringLiteral();
      break;
    case LBRACKET:
      ObjectArray();
      break;
    case TRUE:
      True();
      break;
    case FALSE:
      False();
      break;
    case IDENTIFIER:
    case SHORTHAND:
    case LCURLY:
      Reference();
      break;
    default:
      jj_la1[7] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
  }

/**
 * This method has yet to be fully implemented
 * but will allow arbitrarily nested method
 * calls
 */
  final public void Method() throws ParseException {
                 /*@bgen(jjtree) Method */
  ASTMethod jjtn000 = new ASTMethod(this, JJTMETHOD);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      Identifier();
      jj_consume_token(LPAREN);
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LBRACKET:
      case STRING_LITERAL:
      case TRUE:
      case FALSE:
      case IDENTIFIER:
      case SHORTHAND:
      case LCURLY:
        Parameter();
        label_4:
        while (true) {
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case COMMA:
            ;
            break;
          default:
            jj_la1[8] = jj_gen;
            break label_4;
          }
          jj_consume_token(COMMA);
          Parameter();
        }
        break;
      default:
        jj_la1[9] = jj_gen;
        ;
      }
      jj_consume_token(RPAREN);
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void Reference() throws ParseException {
                    /*@bgen(jjtree) Reference */
  ASTReference jjtn000 = new ASTReference(this, JJTREFERENCE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case SHORTHAND:
        jj_consume_token(SHORTHAND);
        break;
      default:
        jj_la1[10] = jj_gen;
        ;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LCURLY:
        jj_consume_token(LCURLY);
        break;
      default:
        jj_la1[11] = jj_gen;
        ;
      }
      jj_consume_token(IDENTIFIER);
      label_5:
      while (true) {
        if (jj_2_1(2)) {
          ;
        } else {
          break label_5;
        }
        jj_consume_token(DOT);
        if (jj_2_2(3)) {
          Method();
        } else {
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case IDENTIFIER:
            Identifier();
            switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
            case RCURLY:
              jj_consume_token(RCURLY);
              break;
            default:
              jj_la1[12] = jj_gen;
              ;
            }
            break;
          default:
            jj_la1[13] = jj_gen;
            jj_consume_token(-1);
            throw new ParseException();
          }
        }
      }
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void True() throws ParseException {
               /*@bgen(jjtree) True */
  ASTTrue jjtn000 = new ASTTrue(this, JJTTRUE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(TRUE);
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void False() throws ParseException {
                /*@bgen(jjtree) False */
  ASTFalse jjtn000 = new ASTFalse(this, JJTFALSE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(FALSE);
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

/**
 * This method is responsible for allowing
 * all non-grammar text to pass through
 * unscathed.
 */
  final public void Text() throws ParseException {
 /*@bgen(jjtree) Text */
    ASTText jjtn000 = new ASTText(this, JJTTEXT);
    boolean jjtc000 = true;
    jjtree.openNodeScope(jjtn000);Token t;
    try {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case TEXT:
        jj_consume_token(TEXT);
        break;
      case DOT:
        jj_consume_token(DOT);
        break;
      case NUMBER_LITERAL:
        jj_consume_token(NUMBER_LITERAL);
        break;
      case ESCAPE_SEQUENCE:
        t = jj_consume_token(ESCAPE_SEQUENCE);
      jjtree.closeNodeScope(jjtn000, true);
      jjtc000 = false;
        t.image = t.image.substring(1);
        break;
      default:
        jj_la1[14] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

// -----------------------------------------------------------------------
// 
// Directive Syntax
// 
// -----------------------------------------------------------------------
  final public void IfStatement() throws ParseException {
                      /*@bgen(jjtree) IfStatement */
  ASTIfStatement jjtn000 = new ASTIfStatement(this, JJTIFSTATEMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(IF_DIRECTIVE);
      jj_consume_token(LPAREN);
      Expression();
      jj_consume_token(RPAREN);
      ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
      boolean jjtc001 = true;
      jjtree.openNodeScope(jjtn001);
      try {
        label_6:
        while (true) {
          Statement();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case ESCAPE_SEQUENCE:
          case TEXT:
          case SINGLE_LINE_COMMENT:
          case FORMAL_COMMENT:
          case MULTI_LINE_COMMENT:
          case INCLUDE_DIRECTIVE:
          case IF_DIRECTIVE:
          case STOP_DIRECTIVE:
          case NUMBER_LITERAL:
          case WORD:
          case IDENTIFIER:
          case DOT:
          case SHORTHAND:
          case LCURLY:
            ;
            break;
          default:
            jj_la1[15] = jj_gen;
            break label_6;
          }
        }
      } catch (Throwable jjte001) {
      if (jjtc001) {
        jjtree.clearNodeScope(jjtn001);
        jjtc001 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte001 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte001;}
      }
      if (jjte001 instanceof ParseException) {
        {if (true) throw (ParseException)jjte001;}
      }
      {if (true) throw (Error)jjte001;}
      } finally {
      if (jjtc001) {
        jjtree.closeNodeScope(jjtn001, true);
      }
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case ELSEIF_DIRECTIVE:
        label_7:
        while (true) {
          ElseIfStatement();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case ELSEIF_DIRECTIVE:
            ;
            break;
          default:
            jj_la1[16] = jj_gen;
            break label_7;
          }
        }
        break;
      default:
        jj_la1[17] = jj_gen;
        ;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case ELSE_DIRECTIVE:
        ElseStatement();
        break;
      default:
        jj_la1[18] = jj_gen;
        ;
      }
      jj_consume_token(END);
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void ElseStatement() throws ParseException {
                        /*@bgen(jjtree) ElseStatement */
  ASTElseStatement jjtn000 = new ASTElseStatement(this, JJTELSESTATEMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(ELSE_DIRECTIVE);
      ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
      boolean jjtc001 = true;
      jjtree.openNodeScope(jjtn001);
      try {
        label_8:
        while (true) {
          Statement();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case ESCAPE_SEQUENCE:
          case TEXT:
          case SINGLE_LINE_COMMENT:
          case FORMAL_COMMENT:
          case MULTI_LINE_COMMENT:
          case INCLUDE_DIRECTIVE:
          case IF_DIRECTIVE:
          case STOP_DIRECTIVE:
          case NUMBER_LITERAL:
          case WORD:
          case IDENTIFIER:
          case DOT:
          case SHORTHAND:
          case LCURLY:
            ;
            break;
          default:
            jj_la1[19] = jj_gen;
            break label_8;
          }
        }
      } catch (Throwable jjte001) {
      if (jjtc001) {
        jjtree.clearNodeScope(jjtn001);
        jjtc001 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte001 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte001;}
      }
      if (jjte001 instanceof ParseException) {
        {if (true) throw (ParseException)jjte001;}
      }
      {if (true) throw (Error)jjte001;}
      } finally {
      if (jjtc001) {
        jjtree.closeNodeScope(jjtn001, true);
      }
      }
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void ElseIfStatement() throws ParseException {
                          /*@bgen(jjtree) ElseIfStatement */
  ASTElseIfStatement jjtn000 = new ASTElseIfStatement(this, JJTELSEIFSTATEMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      jj_consume_token(ELSEIF_DIRECTIVE);
      jj_consume_token(LPAREN);
      Expression();
      jj_consume_token(RPAREN);
      ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK);
      boolean jjtc001 = true;
      jjtree.openNodeScope(jjtn001);
      try {
        label_9:
        while (true) {
          Statement();
          switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
          case ESCAPE_SEQUENCE:
          case TEXT:
          case SINGLE_LINE_COMMENT:
          case FORMAL_COMMENT:
          case MULTI_LINE_COMMENT:
          case INCLUDE_DIRECTIVE:
          case IF_DIRECTIVE:
          case STOP_DIRECTIVE:
          case NUMBER_LITERAL:
          case WORD:
          case IDENTIFIER:
          case DOT:
          case SHORTHAND:
          case LCURLY:
            ;
            break;
          default:
            jj_la1[20] = jj_gen;
            break label_9;
          }
        }
      } catch (Throwable jjte001) {
      if (jjtc001) {
        jjtree.clearNodeScope(jjtn001);
        jjtc001 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte001 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte001;}
      }
      if (jjte001 instanceof ParseException) {
        {if (true) throw (ParseException)jjte001;}
      }
      {if (true) throw (Error)jjte001;}
      } finally {
      if (jjtc001) {
        jjtree.closeNodeScope(jjtn001, true);
      }
      }
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

/**
 * This method corresponds to an #include
 * directive in a Velocity template. The
 * following are examples of #include
 * constructs that are acceptable in
 * a template:
 *
 * #include "foo.inc" 
 */
  final public void IncludeStatement() throws ParseException {
    jj_consume_token(INCLUDE_DIRECTIVE);
    jj_consume_token(STRING_LITERAL);
  }

/**
 * This method corresponds to the #stop
 * directive which just simulates and EOF
 * so that parsing stops. The #stop directive
 * is really only useful for debugging
 * purposes.
 */
  final public void StopStatement() throws ParseException {
    jj_consume_token(STOP_DIRECTIVE);
  }

// -----------------------------------------------------------------------
// 
// Expression Syntax
// 
// -----------------------------------------------------------------------
  final public void Expression() throws ParseException {
                     /*@bgen(jjtree) Expression */
  ASTExpression jjtn000 = new ASTExpression(this, JJTEXPRESSION);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      if (jj_2_3(2147483647)) {
        Assignment();
      } else {
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case LPAREN:
        case LBRACKET:
        case STRING_LITERAL:
        case TRUE:
        case FALSE:
        case LOGICAL_NOT:
        case NUMBER_LITERAL:
        case IDENTIFIER:
        case SHORTHAND:
        case LCURLY:
          ConditionalOrExpression();
          break;
        default:
          jj_la1[21] = jj_gen;
          jj_consume_token(-1);
          throw new ParseException();
        }
      }
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000, true);
      }
    }
  }

  final public void Assignment() throws ParseException {
                                    /*@bgen(jjtree) #Assignment( 2) */
  ASTAssignment jjtn000 = new ASTAssignment(this, JJTASSIGNMENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
    try {
      PrimaryExpression();
      jj_consume_token(EQUALS);
      Expression();
    } catch (Throwable jjte000) {
      if (jjtc000) {
        jjtree.clearNodeScope(jjtn000);
        jjtc000 = false;
      } else {
        jjtree.popNode();
      }
      if (jjte000 instanceof RuntimeException) {
        {if (true) throw (RuntimeException)jjte000;}
      }
      if (jjte000 instanceof ParseException) {
        {if (true) throw (ParseException)jjte000;}
      }
      {if (true) throw (Error)jjte000;}
    } finally {
      if (jjtc000) {
        jjtree.closeNodeScope(jjtn000,  2);
      }
    }
  }

  final public void ConditionalOrExpression() throws ParseException {
    ConditionalAndExpression();
    label_10:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_OR:
        ;
        break;
      default:
        jj_la1[22] = jj_gen;
        break label_10;
      }
      jj_consume_token(LOGICAL_OR);
                   ASTOrNode jjtn001 = new ASTOrNode(this, JJTORNODE);
                   boolean jjtc001 = true;
                   jjtree.openNodeScope(jjtn001);
      try {
        ConditionalAndExpression();
      } catch (Throwable jjte001) {
                   if (jjtc001) {
                     jjtree.clearNodeScope(jjtn001);
                     jjtc001 = false;
                   } else {
                     jjtree.popNode();
                   }
                   if (jjte001 instanceof RuntimeException) {
                     {if (true) throw (RuntimeException)jjte001;}
                   }
                   if (jjte001 instanceof ParseException) {
                     {if (true) throw (ParseException)jjte001;}
                   }
                   {if (true) throw (Error)jjte001;}
      } finally {
                   if (jjtc001) {
                     jjtree.closeNodeScope(jjtn001,  2);
                   }
      }
    }
  }

  final public void ConditionalAndExpression() throws ParseException {
    EqualityExpression();
    label_11:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_AND:
        ;
        break;
      default:
        jj_la1[23] = jj_gen;
        break label_11;
      }
      jj_consume_token(LOGICAL_AND);
                     ASTAndNode jjtn001 = new ASTAndNode(this, JJTANDNODE);
                     boolean jjtc001 = true;
                     jjtree.openNodeScope(jjtn001);
      try {
        EqualityExpression();
      } catch (Throwable jjte001) {
                     if (jjtc001) {
                       jjtree.clearNodeScope(jjtn001);
                       jjtc001 = false;
                     } else {
                       jjtree.popNode();
                     }
                     if (jjte001 instanceof RuntimeException) {
                       {if (true) throw (RuntimeException)jjte001;}
                     }
                     if (jjte001 instanceof ParseException) {
                       {if (true) throw (ParseException)jjte001;}
                     }
                     {if (true) throw (Error)jjte001;}
      } finally {
                     if (jjtc001) {
                       jjtree.closeNodeScope(jjtn001,  2);
                     }
      }
    }
  }

  final public void EqualityExpression() throws ParseException {
    RelationalExpression();
    label_12:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_EQUALS:
      case LOGICAL_NOT_EQUALS:
        ;
        break;
      default:
        jj_la1[24] = jj_gen;
        break label_12;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_EQUALS:
        jj_consume_token(LOGICAL_EQUALS);
                             ASTEQNode jjtn001 = new ASTEQNode(this, JJTEQNODE);
                             boolean jjtc001 = true;
                             jjtree.openNodeScope(jjtn001);
        try {
          RelationalExpression();
        } catch (Throwable jjte001) {
                             if (jjtc001) {
                               jjtree.clearNodeScope(jjtn001);
                               jjtc001 = false;
                             } else {
                               jjtree.popNode();
                             }
                             if (jjte001 instanceof RuntimeException) {
                               {if (true) throw (RuntimeException)jjte001;}
                             }
                             if (jjte001 instanceof ParseException) {
                               {if (true) throw (ParseException)jjte001;}
                             }
                             {if (true) throw (Error)jjte001;}
        } finally {
                             if (jjtc001) {
                               jjtree.closeNodeScope(jjtn001,  2);
                             }
        }
        break;
      case LOGICAL_NOT_EQUALS:
        jj_consume_token(LOGICAL_NOT_EQUALS);
                                 ASTNENode jjtn002 = new ASTNENode(this, JJTNENODE);
                                 boolean jjtc002 = true;
                                 jjtree.openNodeScope(jjtn002);
        try {
          RelationalExpression();
        } catch (Throwable jjte002) {
                                 if (jjtc002) {
                                   jjtree.clearNodeScope(jjtn002);
                                   jjtc002 = false;
                                 } else {
                                   jjtree.popNode();
                                 }
                                 if (jjte002 instanceof RuntimeException) {
                                   {if (true) throw (RuntimeException)jjte002;}
                                 }
                                 if (jjte002 instanceof ParseException) {
                                   {if (true) throw (ParseException)jjte002;}
                                 }
                                 {if (true) throw (Error)jjte002;}
        } finally {
                                 if (jjtc002) {
                                   jjtree.closeNodeScope(jjtn002,  2);
                                 }
        }
        break;
      default:
        jj_la1[25] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
  }

  final public void RelationalExpression() throws ParseException {
    AdditiveExpression();
    label_13:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_LT:
      case LOGICAL_LE:
      case LOGICAL_GT:
      case LOGICAL_GE:
        ;
        break;
      default:
        jj_la1[26] = jj_gen;
        break label_13;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case LOGICAL_LT:
        jj_consume_token(LOGICAL_LT);
                         ASTLTNode jjtn001 = new ASTLTNode(this, JJTLTNODE);
                         boolean jjtc001 = true;
                         jjtree.openNodeScope(jjtn001);
        try {
          AdditiveExpression();
        } catch (Throwable jjte001) {
                         if (jjtc001) {
                           jjtree.clearNodeScope(jjtn001);
                           jjtc001 = false;
                         } else {
                           jjtree.popNode();
                         }
                         if (jjte001 instanceof RuntimeException) {
                           {if (true) throw (RuntimeException)jjte001;}
                         }
                         if (jjte001 instanceof ParseException) {
                           {if (true) throw (ParseException)jjte001;}
                         }
                         {if (true) throw (Error)jjte001;}
        } finally {
                         if (jjtc001) {
                           jjtree.closeNodeScope(jjtn001,  2);
                         }
        }
        break;
      case LOGICAL_GT:
        jj_consume_token(LOGICAL_GT);
                         ASTGTNode jjtn002 = new ASTGTNode(this, JJTGTNODE);
                         boolean jjtc002 = true;
                         jjtree.openNodeScope(jjtn002);
        try {
          AdditiveExpression();
        } catch (Throwable jjte002) {
                         if (jjtc002) {
                           jjtree.clearNodeScope(jjtn002);
                           jjtc002 = false;
                         } else {
                           jjtree.popNode();
                         }
                         if (jjte002 instanceof RuntimeException) {
                           {if (true) throw (RuntimeException)jjte002;}
                         }
                         if (jjte002 instanceof ParseException) {
                           {if (true) throw (ParseException)jjte002;}
                         }
                         {if (true) throw (Error)jjte002;}
        } finally {
                         if (jjtc002) {
                           jjtree.closeNodeScope(jjtn002,  2);
                         }
        }
        break;
      case LOGICAL_LE:
        jj_consume_token(LOGICAL_LE);
                         ASTLENode jjtn003 = new ASTLENode(this, JJTLENODE);
                         boolean jjtc003 = true;
                         jjtree.openNodeScope(jjtn003);
        try {
          AdditiveExpression();
        } catch (Throwable jjte003) {
                         if (jjtc003) {
                           jjtree.clearNodeScope(jjtn003);
                           jjtc003 = false;
                         } else {
                           jjtree.popNode();
                         }
                         if (jjte003 instanceof RuntimeException) {
                           {if (true) throw (RuntimeException)jjte003;}
                         }
                         if (jjte003 instanceof ParseException) {
                           {if (true) throw (ParseException)jjte003;}
                         }
                         {if (true) throw (Error)jjte003;}
        } finally {
                         if (jjtc003) {
                           jjtree.closeNodeScope(jjtn003,  2);
                         }
        }
        break;
      case LOGICAL_GE:
        jj_consume_token(LOGICAL_GE);
                         ASTGENode jjtn004 = new ASTGENode(this, JJTGENODE);
                         boolean jjtc004 = true;
                         jjtree.openNodeScope(jjtn004);
        try {
          AdditiveExpression();
        } catch (Throwable jjte004) {
                         if (jjtc004) {
                           jjtree.clearNodeScope(jjtn004);
                           jjtc004 = false;
                         } else {
                           jjtree.popNode();
                         }
                         if (jjte004 instanceof RuntimeException) {
                           {if (true) throw (RuntimeException)jjte004;}
                         }
                         if (jjte004 instanceof ParseException) {
                           {if (true) throw (ParseException)jjte004;}
                         }
                         {if (true) throw (Error)jjte004;}
        } finally {
                         if (jjtc004) {
                           jjtree.closeNodeScope(jjtn004,  2);
                         }
        }
        break;
      default:
        jj_la1[27] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
  }

  final public void AdditiveExpression() throws ParseException {
    MultiplicativeExpression();
    label_14:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case MINUS:
      case PLUS:
        ;
        break;
      default:
        jj_la1[28] = jj_gen;
        break label_14;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case PLUS:
        jj_consume_token(PLUS);
                    ASTAddNode jjtn001 = new ASTAddNode(this, JJTADDNODE);
                    boolean jjtc001 = true;
                    jjtree.openNodeScope(jjtn001);
        try {
          MultiplicativeExpression();
        } catch (Throwable jjte001) {
                    if (jjtc001) {
                      jjtree.clearNodeScope(jjtn001);
                      jjtc001 = false;
                    } else {
                      jjtree.popNode();
                    }
                    if (jjte001 instanceof RuntimeException) {
                      {if (true) throw (RuntimeException)jjte001;}
                    }
                    if (jjte001 instanceof ParseException) {
                      {if (true) throw (ParseException)jjte001;}
                    }
                    {if (true) throw (Error)jjte001;}
        } finally {
                    if (jjtc001) {
                      jjtree.closeNodeScope(jjtn001,  2);
                    }
        }
        break;
      case MINUS:
        jj_consume_token(MINUS);
                    ASTSubtractNode jjtn002 = new ASTSubtractNode(this, 
JJTSUBTRACTNODE);
                    boolean jjtc002 = true;
                    jjtree.openNodeScope(jjtn002);
        try {
          MultiplicativeExpression();
        } catch (Throwable jjte002) {
                    if (jjtc002) {
                      jjtree.clearNodeScope(jjtn002);
                      jjtc002 = false;
                    } else {
                      jjtree.popNode();
                    }
                    if (jjte002 instanceof RuntimeException) {
                      {if (true) throw (RuntimeException)jjte002;}
                    }
                    if (jjte002 instanceof ParseException) {
                      {if (true) throw (ParseException)jjte002;}
                    }
                    {if (true) throw (Error)jjte002;}
        } finally {
                    if (jjtc002) {
                      jjtree.closeNodeScope(jjtn002,  2);
                    }
        }
        break;
      default:
        jj_la1[29] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
  }

  final public void MultiplicativeExpression() throws ParseException {
    UnaryExpression();
    label_15:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case MULTIPLY:
      case DIVIDE:
      case MODULUS:
        ;
        break;
      default:
        jj_la1[30] = jj_gen;
        break label_15;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case MULTIPLY:
        jj_consume_token(MULTIPLY);
                       ASTMulNode jjtn001 = new ASTMulNode(this, JJTMULNODE);
                       boolean jjtc001 = true;
                       jjtree.openNodeScope(jjtn001);
        try {
          UnaryExpression();
        } catch (Throwable jjte001) {
                       if (jjtc001) {
                         jjtree.clearNodeScope(jjtn001);
                         jjtc001 = false;
                       } else {
                         jjtree.popNode();
                       }
                       if (jjte001 instanceof RuntimeException) {
                         {if (true) throw (RuntimeException)jjte001;}
                       }
                       if (jjte001 instanceof ParseException) {
                         {if (true) throw (ParseException)jjte001;}
                       }
                       {if (true) throw (Error)jjte001;}
        } finally {
                       if (jjtc001) {
                         jjtree.closeNodeScope(jjtn001,  2);
                       }
        }
        break;
      case DIVIDE:
        jj_consume_token(DIVIDE);
                       ASTDivNode jjtn002 = new ASTDivNode(this, JJTDIVNODE);
                       boolean jjtc002 = true;
                       jjtree.openNodeScope(jjtn002);
        try {
          UnaryExpression();
        } catch (Throwable jjte002) {
                       if (jjtc002) {
                         jjtree.clearNodeScope(jjtn002);
                         jjtc002 = false;
                       } else {
                         jjtree.popNode();
                       }
                       if (jjte002 instanceof RuntimeException) {
                         {if (true) throw (RuntimeException)jjte002;}
                       }
                       if (jjte002 instanceof ParseException) {
                         {if (true) throw (ParseException)jjte002;}
                       }
                       {if (true) throw (Error)jjte002;}
        } finally {
                       if (jjtc002) {
                         jjtree.closeNodeScope(jjtn002,  2);
                       }
        }
        break;
      case MODULUS:
        jj_consume_token(MODULUS);
                       ASTModNode jjtn003 = new ASTModNode(this, JJTMODNODE);
                       boolean jjtc003 = true;
                       jjtree.openNodeScope(jjtn003);
        try {
          UnaryExpression();
        } catch (Throwable jjte003) {
                       if (jjtc003) {
                         jjtree.clearNodeScope(jjtn003);
                         jjtc003 = false;
                       } else {
                         jjtree.popNode();
                       }
                       if (jjte003 instanceof RuntimeException) {
                         {if (true) throw (RuntimeException)jjte003;}
                       }
                       if (jjte003 instanceof ParseException) {
                         {if (true) throw (ParseException)jjte003;}
                       }
                       {if (true) throw (Error)jjte003;}
        } finally {
                       if (jjtc003) {
                         jjtree.closeNodeScope(jjtn003,  2);
                       }
        }
        break;
      default:
        jj_la1[31] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
  }

  final public void UnaryExpression() throws ParseException {
    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
    case LOGICAL_NOT:
      jj_consume_token(LOGICAL_NOT);
                    ASTNotNode jjtn001 = new ASTNotNode(this, JJTNOTNODE);
                    boolean jjtc001 = true;
                    jjtree.openNodeScope(jjtn001);
      try {
        UnaryExpression();
      } catch (Throwable jjte001) {
                    if (jjtc001) {
                      jjtree.clearNodeScope(jjtn001);
                      jjtc001 = false;
                    } else {
                      jjtree.popNode();
                    }
                    if (jjte001 instanceof RuntimeException) {
                      {if (true) throw (RuntimeException)jjte001;}
                    }
                    if (jjte001 instanceof ParseException) {
                      {if (true) throw (ParseException)jjte001;}
                    }
                    {if (true) throw (Error)jjte001;}
      } finally {
                    if (jjtc001) {
                      jjtree.closeNodeScope(jjtn001,  1);
                    }
      }
      break;
    case LPAREN:
    case LBRACKET:
    case STRING_LITERAL:
    case TRUE:
    case FALSE:
    case NUMBER_LITERAL:
    case IDENTIFIER:
    case SHORTHAND:
    case LCURLY:
      PrimaryExpression();
      break;
    default:
      jj_la1[32] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
  }

  final public void PrimaryExpression() throws ParseException {
    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
    case STRING_LITERAL:
      StringLiteral();
      break;
    case NUMBER_LITERAL:
      NumberLiteral();
      break;
    case IDENTIFIER:
    case SHORTHAND:
    case LCURLY:
      Reference();
      break;
    case LBRACKET:
      ObjectArray();
      break;
    case TRUE:
      True();
      break;
    case FALSE:
      False();
      break;
    case LPAREN:
      jj_consume_token(LPAREN);
      Expression();
      jj_consume_token(RPAREN);
      break;
    default:
      jj_la1[33] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
  }

  final private boolean jj_2_1(int xla) {
    jj_la = xla; jj_lastpos = jj_scanpos = token;
    boolean retval = !jj_3_1();
    jj_save(0, xla);
    return retval;
  }

  final private boolean jj_2_2(int xla) {
    jj_la = xla; jj_lastpos = jj_scanpos = token;
    boolean retval = !jj_3_2();
    jj_save(1, xla);
    return retval;
  }

  final private boolean jj_2_3(int xla) {
    jj_la = xla; jj_lastpos = jj_scanpos = token;
    boolean retval = !jj_3_3();
    jj_save(2, xla);
    return retval;
  }

  final private boolean jj_3R_34() {
    if (jj_scan_token(FALSE)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3_3() {
    if (jj_3R_18()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(EQUALS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_48() {
    if (jj_3R_50()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_51()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_19() {
    if (jj_scan_token(IDENTIFIER)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_33() {
    if (jj_scan_token(TRUE)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_47() {
    if (jj_3R_18()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(EQUALS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_35()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3_1() {
    if (jj_scan_token(DOT)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3_2()) {
    jj_scanpos = xsp;
    if (jj_3R_16()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_45() {
    if (jj_3R_48()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_42() {
    if (jj_scan_token(LCURLY)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_35() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_44()) {
    jj_scanpos = xsp;
    if (jj_3R_45()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_44() {
    if (jj_3R_47()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_41() {
    if (jj_scan_token(SHORTHAND)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_31() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_41()) jj_scanpos = xsp;
    else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    xsp = jj_scanpos;
    if (jj_3R_42()) jj_scanpos = xsp;
    else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(IDENTIFIER)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3_1()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_46() {
    if (jj_scan_token(COMMA)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_28()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_17() {
    if (jj_3R_19()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(LPAREN)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_20()) jj_scanpos = xsp;
    else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(RPAREN)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_29() {
    if (jj_scan_token(STRING_LITERAL)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_30() {
    if (jj_scan_token(NUMBER_LITERAL)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_40() {
    if (jj_3R_31()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_39() {
    if (jj_3R_34()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_43() {
    if (jj_3R_28()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_46()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_27() {
    if (jj_scan_token(LPAREN)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_35()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(RPAREN)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_38() {
    if (jj_3R_33()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_26() {
    if (jj_3R_34()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_37() {
    if (jj_3R_32()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_25() {
    if (jj_3R_33()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_28() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_36()) {
    jj_scanpos = xsp;
    if (jj_3R_37()) {
    jj_scanpos = xsp;
    if (jj_3R_38()) {
    jj_scanpos = xsp;
    if (jj_3R_39()) {
    jj_scanpos = xsp;
    if (jj_3R_40()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_36() {
    if (jj_3R_29()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_24() {
    if (jj_3R_32()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_23() {
    if (jj_3R_31()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_22() {
    if (jj_3R_30()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_21() {
    if (jj_3R_29()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_18() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_21()) {
    jj_scanpos = xsp;
    if (jj_3R_22()) {
    jj_scanpos = xsp;
    if (jj_3R_23()) {
    jj_scanpos = xsp;
    if (jj_3R_24()) {
    jj_scanpos = xsp;
    if (jj_3R_25()) {
    jj_scanpos = xsp;
    if (jj_3R_26()) {
    jj_scanpos = xsp;
    if (jj_3R_27()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_72() {
    if (jj_3R_18()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_75() {
    if (jj_scan_token(MODULUS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_67()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_71() {
    if (jj_scan_token(LOGICAL_NOT)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_67()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_67() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_71()) {
    jj_scanpos = xsp;
    if (jj_3R_72()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_74() {
    if (jj_scan_token(DIVIDE)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_67()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_32() {
    if (jj_scan_token(LBRACKET)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_43()) jj_scanpos = xsp;
    else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_scan_token(RBRACKET)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_73() {
    if (jj_scan_token(MULTIPLY)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_67()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_68() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_73()) {
    jj_scanpos = xsp;
    if (jj_3R_74()) {
    jj_scanpos = xsp;
    if (jj_3R_75()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_49() {
    if (jj_scan_token(RCURLY)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_70() {
    if (jj_scan_token(MINUS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_61()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_61() {
    if (jj_3R_67()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_68()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_69() {
    if (jj_scan_token(PLUS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_61()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_62() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_69()) {
    jj_scanpos = xsp;
    if (jj_3R_70()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_16() {
    if (jj_3R_19()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_49()) jj_scanpos = xsp;
    else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_66() {
    if (jj_scan_token(LOGICAL_GE)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_57()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_57() {
    if (jj_3R_61()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_62()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_65() {
    if (jj_scan_token(LOGICAL_LE)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_57()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_64() {
    if (jj_scan_token(LOGICAL_GT)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_57()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_63() {
    if (jj_scan_token(LOGICAL_LT)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_57()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_58() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_63()) {
    jj_scanpos = xsp;
    if (jj_3R_64()) {
    jj_scanpos = xsp;
    if (jj_3R_65()) {
    jj_scanpos = xsp;
    if (jj_3R_66()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_60() {
    if (jj_scan_token(LOGICAL_NOT_EQUALS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_55()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_55() {
    if (jj_3R_57()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_58()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_59() {
    if (jj_scan_token(LOGICAL_EQUALS)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_55()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_56() {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_59()) {
    jj_scanpos = xsp;
    if (jj_3R_60()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_54() {
    if (jj_scan_token(COMMA)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_28()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_52() {
    if (jj_3R_55()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_56()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3_2() {
    if (jj_3R_17()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_53() {
    if (jj_scan_token(LOGICAL_AND)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_52()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  final private boolean jj_3R_50() {
    if (jj_3R_52()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_53()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_20() {
    if (jj_3R_28()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    Token xsp;
    while (true) {
      xsp = jj_scanpos;
      if (jj_3R_54()) { jj_scanpos = xsp; break; }
      if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    }
    return false;
  }

  final private boolean jj_3R_51() {
    if (jj_scan_token(LOGICAL_OR)) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    if (jj_3R_50()) return true;
    if (jj_la == 0 && jj_scanpos == jj_lastpos) return false;
    return false;
  }

  public ParserTokenManager token_source;
  ASCII_CharStream jj_input_stream;
  public Token token, jj_nt;
  private int jj_ntk;
  private Token jj_scanpos, jj_lastpos;
  private int jj_la;
  public boolean lookingAhead = false;
  private boolean jj_semLA;
  private int jj_gen;
  final private int[] jj_la1 = new int[34];
  final private int[] jj_la1_0 = 
{0x70006,0x70006,0x70000,0x6a8,0x70006,0x100,0x6a0,0x6a0,0x100,0x6a0,0x0,0x0,0x0,0x0,0x6,0x70006,0x0,0x0,0x0,0x70006,0x70006,0x6a8,0x20000000,0x10000000,0x0,0x0,0xc0000000,0xc0000000,0x1800000,0x1800000,0xe000000,0xe000000,0x6a8,0x6a8,};
  final private int[] jj_la1_1 = 
{0x78a980,0x78a980,0x0,0x68a010,0x78a980,0x0,0x680000,0x680000,0x0,0x680000,0x200000,0x400000,0x800000,0x80000,0x102000,0x78a980,0x200,0x200,0x400,0x78a980,0x78a980,0x682010,0x0,0x0,0xc,0xc,0x3,0x3,0x0,0x0,0x0,0x0,0x682010,0x682000,};
  final private JJCalls[] jj_2_rtns = new JJCalls[3];
  private boolean jj_rescan = false;
  private int jj_gc = 0;

  public Parser(java.io.InputStream stream) {
    jj_input_stream = new ASCII_CharStream(stream, 1, 1);
    token_source = new ParserTokenManager(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  public void ReInit(java.io.InputStream stream) {
    jj_input_stream.ReInit(stream, 1, 1);
    token_source.ReInit(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  public Parser(java.io.Reader stream) {
    jj_input_stream = new ASCII_CharStream(stream, 1, 1);
    token_source = new ParserTokenManager(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  public void ReInit(java.io.Reader stream) {
    jj_input_stream.ReInit(stream, 1, 1);
    token_source.ReInit(jj_input_stream);
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  public Parser(ParserTokenManager tm) {
    token_source = tm;
    token = new Token();
    jj_ntk = -1;
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  public void ReInit(ParserTokenManager tm) {
    token_source = tm;
    token = new Token();
    jj_ntk = -1;
    jjtree.reset();
    jj_gen = 0;
    for (int i = 0; i < 34; i++) jj_la1[i] = -1;
    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  final private Token jj_consume_token(int kind) throws ParseException {
    Token oldToken;
    if ((oldToken = token).next != null) token = token.next;
    else token = token.next = token_source.getNextToken();
    jj_ntk = -1;
    if (token.kind == kind) {
      jj_gen++;
      if (++jj_gc > 100) {
        jj_gc = 0;
        for (int i = 0; i < jj_2_rtns.length; i++) {
          JJCalls c = jj_2_rtns[i];
          while (c != null) {
            if (c.gen < jj_gen) c.first = null;
            c = c.next;
          }
        }
      }
      return token;
    }
    token = oldToken;
    jj_kind = kind;
    throw generateParseException();
  }

  final private boolean jj_scan_token(int kind) {
    if (jj_scanpos == jj_lastpos) {
      jj_la--;
      if (jj_scanpos.next == null) {
        jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
      } else {
        jj_lastpos = jj_scanpos = jj_scanpos.next;
      }
    } else {
      jj_scanpos = jj_scanpos.next;
    }
    if (jj_rescan) {
      int i = 0; Token tok = token;
      while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
      if (tok != null) jj_add_error_token(kind, i);
    }
    return (jj_scanpos.kind != kind);
  }

  final public Token getNextToken() {
    if (token.next != null) token = token.next;
    else token = token.next = token_source.getNextToken();
    jj_ntk = -1;
    jj_gen++;
    return token;
  }

  final public Token getToken(int index) {
    Token t = lookingAhead ? jj_scanpos : token;
    for (int i = 0; i < index; i++) {
      if (t.next != null) t = t.next;
      else t = t.next = token_source.getNextToken();
    }
    return t;
  }

  final private int jj_ntk() {
    if ((jj_nt=token.next) == null)
      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
    else
      return (jj_ntk = jj_nt.kind);
  }

  private java.util.Vector jj_expentries = new java.util.Vector();
  private int[] jj_expentry;
  private int jj_kind = -1;
  private int[] jj_lasttokens = new int[100];
  private int jj_endpos;

  private void jj_add_error_token(int kind, int pos) {
    if (pos >= 100) return;
    if (pos == jj_endpos + 1) {
      jj_lasttokens[jj_endpos++] = kind;
    } else if (jj_endpos != 0) {
      jj_expentry = new int[jj_endpos];
      for (int i = 0; i < jj_endpos; i++) {
        jj_expentry[i] = jj_lasttokens[i];
      }
      boolean exists = false;
      for (java.util.Enumeration enum = jj_expentries.elements(); 
enum.hasMoreElements();) {
        int[] oldentry = (int[])(enum.nextElement());
        if (oldentry.length == jj_expentry.length) {
          exists = true;
          for (int i = 0; i < jj_expentry.length; i++) {
            if (oldentry[i] != jj_expentry[i]) {
              exists = false;
              break;
            }
          }
          if (exists) break;
        }
      }
      if (!exists) jj_expentries.addElement(jj_expentry);
      if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
    }
  }

  final public ParseException generateParseException() {
    jj_expentries.removeAllElements();
    boolean[] la1tokens = new boolean[57];
    for (int i = 0; i < 57; i++) {
      la1tokens[i] = false;
    }
    if (jj_kind >= 0) {
      la1tokens[jj_kind] = true;
      jj_kind = -1;
    }
    for (int i = 0; i < 34; i++) {
      if (jj_la1[i] == jj_gen) {
        for (int j = 0; j < 32; j++) {
          if ((jj_la1_0[i] & (1<<j)) != 0) {
            la1tokens[j] = true;
          }
          if ((jj_la1_1[i] & (1<<j)) != 0) {
            la1tokens[32+j] = true;
          }
        }
      }
    }
    for (int i = 0; i < 57; i++) {
      if (la1tokens[i]) {
        jj_expentry = new int[1];
        jj_expentry[0] = i;
        jj_expentries.addElement(jj_expentry);
      }
    }
    jj_endpos = 0;
    jj_rescan_token();
    jj_add_error_token(0, 0);
    int[][] exptokseq = new int[jj_expentries.size()][];
    for (int i = 0; i < jj_expentries.size(); i++) {
      exptokseq[i] = (int[])jj_expentries.elementAt(i);
    }
    return new ParseException(token, exptokseq, tokenImage);
  }

  final public void enable_tracing() {
  }

  final public void disable_tracing() {
  }

  final private void jj_rescan_token() {
    jj_rescan = true;
    for (int i = 0; i < 3; i++) {
      JJCalls p = jj_2_rtns[i];
      do {
        if (p.gen > jj_gen) {
          jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
          switch (i) {
            case 0: jj_3_1(); break;
            case 1: jj_3_2(); break;
            case 2: jj_3_3(); break;
          }
        }
        p = p.next;
      } while (p != null);
    }
    jj_rescan = false;
  }

  final private void jj_save(int index, int xla) {
    JJCalls p = jj_2_rtns[index];
    while (p.gen > jj_gen) {
      if (p.next == null) { p = p.next = new JJCalls(); break; }
      p = p.next;
    }
    p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
  }

  static final class JJCalls {
    int gen;
    Token first;
    int arg;
    JJCalls next;
  }

}
/**
 *  VelocimacroFactory.java
 *
 *   makes VM proxy Directive objects
 *
 *   Note : for now, the VM's are hardwired.  Expect they will come either
 *   from a central library, or if I get my way, can be dynamically added.
 *   This implementation should support definition of VM's on the fly w/in vm
 *   template files.
 *
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
 *
 */

package org.apache.velocity.runtime.parser;

import java.util.Hashtable;

import org.apache.velocity.runtime.directive.Directive;
import org.apache.velocity.runtime.directive.VelocimacroProxy;

public class VelocimacroFactory
{
    Hashtable hMacros = new Hashtable();

    /**
     *  Builds the factory macro library
     *
     *  hardwired for now...
     *
     *  also, because of my cheesy macro expander, you must put spaces
     *  surrounding the macro args w/in the definition
     *  
     *  RIGHT :   #foo(x) :=  #if( x ) blargh: x #end
     *  WRONG :   #foo(x) :=  #if(x) blargh:x #end
     * 
     *  this will be fixed if this project gets approved
     */
    VelocimacroFactory()
    {
        Hashtable h;

        /*
         *  pretend these are coming from a libraray db of some sort...
         *
         *  note that they can be dynamically added during template parsing if we 
wished...
         */
        
        /*
         *  the point of #isnull is to show that we can have other velocity
         *  directives w/in the macro, and all renders properly
         *  also, has 2 args
         */
        
        addMacro( "isnull", 2,  "isnull(x y) := #if( x ) x #else y #end" );

        /*
         *  this is just to show having 2 args.  Was first one...
         */
        
        addMacro( "colortext", 2, "colortext(x y) := <font color = x > y </font>");

        /*
         *  shows a 1 arg macro, to show that the arg stuff is really dynamic
         */
        
        addMacro( "boldface", 1,  "boldface(x) := <b> x </b>");

    }

    /**
     *   adds a macro to the factory.  Lots of room for improvement here...
     */
    public void addMacro( String strName, int iArgCount, String strMacro )
    {
        Hashtable h = new Hashtable();
        h.put("macroname", strName);
        h.put("argcount", new Integer( iArgCount ));
        h.put("macrocode",  strMacro);
        
        hMacros.put( strName, h );
        
        return;
    }

    /**
     *   tells the world if a given directive string is a Velocimacro
     */
    public boolean isVelocimacro( String vm )
    {
        if (hMacros.get( vm ) != null)
            return true;

        return false;
    }

    /**
     *  actual factory : creates a Directive that will
     *  behave correctly wrt getting the framework to 
     *  dig out the correct # of args
     */
    public Directive get( String strVMName )
    {
        if ( isVelocimacro( strVMName ) ) {
        
            Hashtable h = (Hashtable) hMacros.get( strVMName );

            VelocimacroProxy vp = new VelocimacroProxy();
        
            vp.setName( (String ) h.get("macroname"));
            vp.setArgCount( ( (Integer)  h.get("argcount")).intValue() );
            vp.setMacroCode( (String)  h.get("macrocode"));
            
            return vp;
        }

        /*
         *  wasn't a VM.  Sorry...
         */

        return null;
    }
}







/**
 *  VelocimacroProxy.java
 *
 *   a proxy Directive object to fit with the current directive system
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
 *
 */

package org.apache.velocity.runtime.directive;

import java.util.Map;

import java.io.Writer;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Hashtable;

import org.apache.velocity.Context;
import org.apache.velocity.util.ClassUtils;

import org.apache.velocity.runtime.Runtime;

import org.apache.velocity.runtime.parser.Node;
import org.apache.velocity.runtime.parser.SimpleNode;
import org.apache.velocity.runtime.parser.Token;
import org.apache.velocity.runtime.parser.ASTReference;
import org.apache.velocity.runtime.parser.ParserTreeConstants;

public class VelocimacroProxy implements Directive
{
    protected String property;
    
    private String strMacroName_ = "";
    private String strMacro_ = "";
    private int    iArgCount_ = 0;

    /**
     *   sets the directive name that this VM will be. Used by Factory
     */
    public void setName( String strName )
    {
        strMacroName_ = strName;
    }

    /**
     *  sets the # of args that this VM will need. Used by Factory.
     */
    public void setArgCount( int iArgs )
    {
        iArgCount_ = iArgs;
    }

    /**
     *  sets  the macro definition. Used by factory.
     */
    public void setMacroCode(String strMacro)
    {
        strMacro_ = strMacro;
    }

    public String getName() { return  strMacroName_; }
    public int getType() { return LINE; }
    public int getArgs() { return iArgCount_; }

    public void render(Context context, Writer writer, Node node)
        throws IOException
    {
        /*
         *  how many args did we get?
         *  Node struct : me->DirectiveArgs->child 0->reference
         */

        int i  = node.jjtGetChild(0).jjtGetNumChildren();

        /*
         *   if we don't have enough children specified...?
         */

        if ( iArgCount_ != i ) {
            /*
             *  do something?
             */
        }

        /*
         *  ok, iterate through and build the list of rendered values.
         *
         *  remember, we are nothing but a renderer.  We don't 'think', ever
         *  like #foreach does
         */

        Vector vArgs = new Vector();

        for (int j = 0; j < i; j++) {

            Node arg  = node.jjtGetChild(0).jjtGetChild(j).jjtGetChild(0);
            //Object value = arg.value(context);

            String value = arg.getFirstToken().image;

            /*
             *  need to understand the tree a bit more.  Will I *always* get a value 
object back?
             */

            if (value == null)
                vArgs.addElement("");
            else
                vArgs.addElement( value ); // value.toString() );
        }

        /*
         *   debug
         */

        //for (int j = 0; j < vArgs.size(); j++)
        //  System.out.println("VelocimacroProxy.render() : " 
        //         + strMacroName_ + "(" + i + ")  arg" + j + " : " +  
vArgs.elementAt(j));

        /*
         *  now, expand our macro out to a string
         */

        String strExpanded = expandMacro( vArgs );

        /*
         *  ok. I have the expanded macro.
         *  now, all I have to do  is let the parser render it
         */


        try {
            ByteArrayInputStream  inStream = new ByteArrayInputStream( 
strExpanded.getBytes() );

            SimpleNode document = Runtime.parse( inStream );
            
            StringWriter sw = new StringWriter();

            document.render(context, sw );

            writer.write(  sw.toString() );
        } catch ( Exception e ) {
            System.out.println("VelocimacroProxy.render() : exception " + e);
            writer.write("oof!" + strMacroName_ );
        }
    }

    /**
     *   expands our macro out given our arg list. KISS for now.
     *
     */
    
    private String expandMacro( Vector vArgs )
    {
        /*
         *   I am sure I should go off and embed someone elses..
         */

        /*
         *  hm.  lets get the definition part
         */
        
        int iWhere = strMacro_.indexOf(":=");

        if (iWhere == -1) {
            // error. malformed macro
        }

        /*
         *  get the proto to show us the targets
         *  and the definition to fill in
         */

        String strProto = strMacro_.substring(0, iWhere - 1);
        String strDef = strMacro_.substring( iWhere+2, strMacro_.length());

        /*
         *  now, find the 'targets'
         */

        iWhere = strProto.indexOf("(");

        if (iWhere == -1) {
            // error. malformed macro
        }

        int iEnd = strProto.indexOf(")");

        if (iEnd == -1) {
            // error. malformed macro
        }

        String strArgList = strProto.substring( iWhere + 1, iEnd );

        /*
         *  now break this into individual elements, and tuck its value in a hash or 
something
         */

        StringTokenizer st = new StringTokenizer( strArgList);

        int iArg = 0;
        Hashtable hArg = new Hashtable();
        Vector vArgToken = new Vector();

        while( st.hasMoreTokens() ) {
            
            String str = st.nextToken();
            
            vArgToken.addElement( str );
            hArg.put( str, vArgs.elementAt( iArg ));
            iArg++;
        }
     
        /*
         *  debug
         */

        //for (int k = 0; k < vArgToken.size(); k++) {
        //   System.out.println(" argtoken " + vArgToken.elementAt(k) + 
        //                     " = " + hArg.get( vArgToken.elementAt(k)));
        //}

        /*
         *  now run through the def and replace each token with it's value
         */

        StringTokenizer st2 = new StringTokenizer(strDef );

        String strOut = "";

        while( st2.hasMoreTokens() ) {

            String strTok = st2.nextToken();

            String strVal = (String) hArg.get( strTok );

            if (strVal != null)
                strOut = strOut + strVal;
            else
                strOut = strOut + " " + strTok;
        }

        //return "->" + strProto + "<-[" + strArgList + "]->" + strDef + "<- ** " + 
strOut + " **";
    
        return strOut;
    }
}








#set $email = "'[EMAIL PROTECTED]'" #set $nullval = "'n/a'" Here is an input that uses the isnull velocimacro to check if the reference is valid :

Here is the same with an invalid reference :

Here is the expansion #set $color = "blue" #set $text = "this is the text" #colortext $color "text" And finally, a simple 1 arg macro #set $boldfacethis = "Boldly going where no template engine... er, sorrry" #boldface $boldfacethis And finally, finally, something that is just a word #iamnotavm

Reply via email to