Seriously unbelievable. Did i say i'd buy you a drink should you ever come
to town? Well, also, https://cdn.meme.am/instances/500x/53443254.jpg

Of course, Mike, the window isn't closed. Until 2.0 releases, there's still
time! We could argue about the default! Or if there's missing options! Or
anything else that no one (except Claude) will actually invest time into!

On Tue, Aug 30, 2016 at 9:28 AM, Mike Kienenberger <mkien...@gmail.com>
wrote:

> What?  You would dare to impose your view of configurable whitespace
> gobbling upon the rest of us after only a mere decade of discussion?
>
> What will we talk about for the next 10 years? :-)
>
> On Tue, Aug 30, 2016 at 12:18 PM,  <cbris...@apache.org> wrote:
> > Author: cbrisson
> > Date: Tue Aug 30 16:18:33 2016
> > New Revision: 1758416
> >
> > URL: http://svn.apache.org/viewvc?rev=1758416&view=rev
> > Log:
> > [engine] Add a configurable space gobbling feature, to control
> indentation in the generated code.
> >
> > Possible values for the 'space.gobbling' configuration key:
> >
> >  - none : no space gobbling at all
> >  - bc : Velocity 1.x backward compatible space gobbling
> >  - lines (the default) : gobbles whitespaces and endline from lines
> containing a single VTL directive
> >  - structured (beta stage) : like 'lines', but also fixes indentation in
> embedded text blocks
> >
> > The commit also includes some lookahead optimizations and cleaning in
> the javacc parser code.
> >
> >
> > Added:
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/IndentationFixer.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/SpaceGobblingTestCase.java
> >       - copied, changed from r1754151, velocity/engine/trunk/
> velocity-engine-core/src/test/java/org/apache/velocity/test/
> util/introspection/ConversionHandlerTestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_smart.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_smart.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_smart.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_smart.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_structured.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_structured.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_structured.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/foreach_structured.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/if.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/if.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/if.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/if.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/macro.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/macro.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/macro.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/macro.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/set.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/set.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/set.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/set.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/structured.vtl.BC
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/structured.vtl.NONE
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/structured.vtl.SMART
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/compare/structured.vtl.STRUCTURED
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/foreach_smart.vtl
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/foreach_structured.vtl
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/if.vtl
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/macro.vtl
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/set.vtl
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/gobbling/structured.vtl
> > Modified:
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeConstants.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeInstance.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeServices.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTBlock.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTDirective.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTElseIfStatement.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTIfStatement.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTText.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/NodeUtils.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ParserVisitor.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/visitor/BaseVisitor.java
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> parser/Parser.jjt
> >     velocity/engine/trunk/velocity-engine-core/src/main/
> resources/org/apache/velocity/runtime/defaults/velocity.properties
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/InlineScopeVMTestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/ScopeTestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/TemplateTestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/issues/Velocity615TestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/issues/Velocity631TestCase.java
> >     velocity/engine/trunk/velocity-engine-core/src/test/
> resources/conversion/compare/matrix.cmp
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeConstants.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/RuntimeConstants.java?rev=1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeConstants.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeConstants.java Tue Aug 30
> 16:18:33 2016
> > @@ -266,6 +266,19 @@ public interface RuntimeConstants
> >       */
> >      String PARSER_POOL_SIZE = "parser.pool.size";
> >
> > +    /**
> > +     * Space gobbling mode
> > +     */
> > +    String SPACE_GOBBLING = "space.gobbling";
> > +
> > +    /**
> > +     * Space gobbling modes
> > +     */
> > +    public enum SpaceGobbling
> > +    {
> > +        NONE, BC, LINES, STRUCTURED
> > +    }
> > +
> >      /*
> >       * ------------------------------------------------------------
> ----------
> >       * These constants are used internally by the Velocity runtime i.e.
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeInstance.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/RuntimeInstance.java?rev=1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeInstance.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeInstance.java Tue Aug 30 16:18:33
> 2016
> > @@ -55,7 +55,6 @@ import org.apache.velocity.util.introspe
> >  import org.slf4j.Logger;
> >  import org.slf4j.LoggerFactory;
> >
> > -import java.io.File;
> >  import java.io.IOException;
> >  import java.io.InputStream;
> >  import java.io.Reader;
> > @@ -66,6 +65,7 @@ import java.util.HashMap;
> >  import java.util.Hashtable;
> >  import java.util.List;
> >  import java.util.Map;
> > +import java.util.NoSuchElementException;
> >  import java.util.Properties;
> >
> >  /**
> > @@ -193,6 +193,11 @@ public class RuntimeInstance implements
> >      private Uberspect uberSpect;
> >      private String encoding;
> >
> > +    /*
> > +     * Space gobbling mode
> > +     */
> > +    private SpaceGobbling spaceGobbling;
> > +
> >      /**
> >       * Creates a new RuntimeInstance object.
> >       */
> > @@ -334,7 +339,19 @@ public class RuntimeInstance implements
> >       */
> >      private void initializeSelfProperties()
> >      {
> > +        /* initialize string interning (defaults to false) */
> >          stringInterning = getBoolean(RUNTIME_STRING_INTERNING, true);
> > +
> > +        /* initialize indentation mode (defaults to 'lines') */
> > +        String im = getString(SPACE_GOBBLING, "lines");
> > +        try
> > +        {
> > +            spaceGobbling = SpaceGobbling.valueOf(im.toUpperCase());
> > +        }
> > +        catch (NoSuchElementException nse)
> > +        {
> > +            spaceGobbling = SpaceGobbling.LINES;
> > +        }
> >      }
> >
> >      /**
> > @@ -1847,4 +1864,13 @@ public class RuntimeInstance implements
> >      {
> >          return stringInterning;
> >      }
> > +
> > +    /**
> > +     * get space gobbling mode
> > +     * @return indentation mode
> > +     */
> > +    public SpaceGobbling getSpaceGobbling()
> > +    {
> > +        return spaceGobbling;
> > +    }
> >  }
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeServices.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/RuntimeServices.java?rev=1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeServices.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/RuntimeServices.java Tue Aug 30 16:18:33
> 2016
> > @@ -26,6 +26,7 @@ import org.apache.velocity.context.Conte
> >  import org.apache.velocity.exception.MethodInvocationException;
> >  import org.apache.velocity.exception.ParseErrorException;
> >  import org.apache.velocity.exception.ResourceNotFoundException;
> > +import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
> >  import org.apache.velocity.runtime.directive.Directive;
> >  import org.apache.velocity.runtime.directive.Macro;
> >  import org.apache.velocity.runtime.parser.ParseException;
> > @@ -34,7 +35,6 @@ import org.apache.velocity.runtime.parse
> >  import org.apache.velocity.runtime.parser.node.SimpleNode;
> >  import org.apache.velocity.runtime.resource.ContentResource;
> >  import org.apache.velocity.util.ExtProperties;
> > -import org.apache.velocity.util.introspection.Introspector;
> >  import org.apache.velocity.util.introspection.Uberspect;
> >  import org.slf4j.Logger;
> >
> > @@ -466,4 +466,6 @@ public interface RuntimeServices
> >      public Directive getDirective(String name);
> >
> >      public boolean useStringInterning();
> > +
> > +    public SpaceGobbling getSpaceGobbling();
> >  }
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTBlock.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTBlock.java?rev=1758416&r1=1758415&
> r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTBlock.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTBlock.java Tue Aug 30
> 16:18:33 2016
> > @@ -24,6 +24,7 @@ import org.apache.velocity.exception.Met
> >  import org.apache.velocity.exception.ParseErrorException;
> >  import org.apache.velocity.exception.ResourceNotFoundException;
> >  import org.apache.velocity.exception.TemplateInitException;
> > +import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
> >  import org.apache.velocity.runtime.parser.Parser;
> >
> >  import java.io.IOException;
> > @@ -35,6 +36,12 @@ import java.io.Writer;
> >   */
> >  public class ASTBlock extends SimpleNode
> >  {
> > +    private String prefix = "";
> > +    private String postfix = "";
> > +
> > +    // used during parsing
> > +    public boolean endsWithNewline = false;
> > +
> >      /**
> >       * @param id
> >       */
> > @@ -61,29 +68,76 @@ public class ASTBlock extends SimpleNode
> >      }
> >
> >      /**
> > +     * @throws TemplateInitException
> > +     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > +     */
> > +    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > +    {
> > +        Object obj = super.init(context, data);
> > +        cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > +        return obj;
> > +    }
> > +
> > +    /**
> > +     * set indentation prefix
> > +     * @param prefix
> > +     */
> > +    public void setPrefix(String prefix)
> > +    {
> > +        this.prefix = prefix;
> > +    }
> > +
> > +    /**
> > +     * get indentation prefix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPrefix()
> > +    {
> > +        return prefix;
> > +    }
> > +
> > +    /**
> > +     * set indentation postfix
> > +     * @param postfix
> > +     */
> > +    public void setPostfix(String postfix)
> > +    {
> > +        this.postfix = postfix;
> > +    }
> > +
> > +    /**
> > +     * get indentation postfix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPostfix()
> > +    {
> > +        return postfix;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#render(
> org.apache.velocity.context.InternalContextAdapter, java.io.Writer)
> >       */
> >      public boolean render( InternalContextAdapter context, Writer
> writer)
> >          throws IOException, MethodInvocationException,
> >                 ResourceNotFoundException, ParseErrorException
> >      {
> > +        SpaceGobbling spaceGobbling = rsvc.getSpaceGobbling();
> > +
> > +        if (spaceGobbling == SpaceGobbling.NONE)
> > +        {
> > +            writer.write(prefix);
> > +        }
> > +
> >          int i, k = jjtGetNumChildren();
> >
> >          for (i = 0; i < k; i++)
> >              jjtGetChild(i).render(context, writer);
> >
> > +        if (spaceGobbling.compareTo(SpaceGobbling.LINES) < 0)
> > +        {
> > +            writer.write(postfix);
> > +        }
> > +
> >          return true;
> >      }
> > -
> > -    /**
> > -     * @throws TemplateInitException
> > -     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > -     */
> > -    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > -    {
> > -       Object obj = super.init(context, data);
> > -       cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > -       return obj;
> > -    }
> > -
> >  }
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTDirective.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTDirective.java?rev=1758416&
> r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTDirective.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTDirective.java Tue Aug 30
> 16:18:33 2016
> > @@ -25,11 +25,14 @@ import org.apache.velocity.exception.Par
> >  import org.apache.velocity.exception.ResourceNotFoundException;
> >  import org.apache.velocity.exception.TemplateInitException;
> >  import org.apache.velocity.exception.VelocityException;
> > +import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
> >  import org.apache.velocity.runtime.directive.BlockMacro;
> >  import org.apache.velocity.runtime.directive.Directive;
> >  import org.apache.velocity.runtime.directive.RuntimeMacro;
> >  import org.apache.velocity.runtime.parser.ParseException;
> >  import org.apache.velocity.runtime.parser.Parser;
> > +import org.apache.velocity.runtime.parser.ParserConstants;
> > +import org.apache.velocity.runtime.parser.Token;
> >
> >  import java.io.IOException;
> >  import java.io.Writer;
> > @@ -55,6 +58,9 @@ public class ASTDirective extends Simple
> >      private boolean isDirective;
> >      private boolean isInitialized;
> >
> > +    private String prefix = "";
> > +    private String postfix = "";
> > +
> >      /**
> >       * @param id
> >       */
> > @@ -121,7 +127,9 @@ public class ASTDirective extends Simple
> >                              e);
> >                  }
> >
> > -                directive.setLocation(getLine(), getColumn(),
> getTemplate());
> > +                Token t = first;
> > +                if (t.kind == ParserConstants.WHITESPACE) t = t.next;
> > +                directive.setLocation(t.beginLine, t.beginColumn,
> getTemplate());
> >                  directive.init(rsvc, context, this);
> >              }
> >              else if( directiveName.startsWith("@") )
> > @@ -191,28 +199,87 @@ public class ASTDirective extends Simple
> >              saveTokenImages();
> >              cleanupParserAndTokens();
> >          }
> > +
> > +        if (rsvc.getSpaceGobbling() == SpaceGobbling.STRUCTURED &&
> isInitialized && isDirective && directive.getType() == Directive.BLOCK)
> > +        {
> > +            NodeUtils.fixIndentation(this, prefix);
> > +        }
> >
> >          return data;
> >      }
> >
> >      /**
> > +     * set indentation prefix
> > +     * @param prefix
> > +     */
> > +    public void setPrefix(String prefix)
> > +    {
> > +        this.prefix = prefix;
> > +    }
> > +
> > +    /**
> > +     * get indentation prefix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPrefix()
> > +    {
> > +        return prefix;
> > +    }
> > +
> > +    /**
> > +     * set indentation postfix
> > +     * @param postfix
> > +     */
> > +    public void setPostfix(String postfix)
> > +    {
> > +        this.postfix = postfix;
> > +    }
> > +
> > +    public int getDirectiveType()
> > +    {
> > +        return directive.getType();
> > +    }
> > +
> > +    /**
> > +     * get indentation postfix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPostfix()
> > +    {
> > +        return postfix;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#render(
> org.apache.velocity.context.InternalContextAdapter, java.io.Writer)
> >       */
> >      public boolean render( InternalContextAdapter context, Writer
> writer)
> >          throws IOException,MethodInvocationException,
> ResourceNotFoundException, ParseErrorException
> >      {
> > +        SpaceGobbling spaceGobbling = rsvc.getSpaceGobbling();
> >          /*
> >           *  normal processing
> >           */
> >
> >          if (isDirective)
> >          {
> > +            if (spaceGobbling.compareTo(SpaceGobbling.LINES) < 0)
> > +            {
> > +                writer.write(prefix);
> > +            }
> > +
> >              directive.render(context, writer, this);
> > +
> > +            if (spaceGobbling == SpaceGobbling.NONE)
> > +            {
> > +                writer.write(postfix);
> > +            }
> >          }
> >          else
> >          {
> > +            writer.write(prefix);
> >              writer.write( "#");
> > -            writer.write( directiveName );
> > +            writer.write(directiveName);
> > +            writer.write(postfix);
> >          }
> >
> >          return true;
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTElseIfStatement.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTElseIfStatement.java?rev=
> 1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTElseIfStatement.java
> (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTElseIfStatement.java Tue
> Aug 30 16:18:33 2016
> > @@ -24,6 +24,7 @@ import org.apache.velocity.exception.Met
> >  import org.apache.velocity.exception.ParseErrorException;
> >  import org.apache.velocity.exception.ResourceNotFoundException;
> >  import org.apache.velocity.exception.TemplateInitException;
> > +import org.apache.velocity.runtime.RuntimeConstants;
> >  import org.apache.velocity.runtime.parser.Parser;
> >
> >  import java.io.IOException;
> > @@ -59,6 +60,17 @@ public class ASTElseIfStatement extends
> >      }
> >
> >      /**
> > +     * @throws TemplateInitException
> > +     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > +     */
> > +    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > +    {
> > +        Object obj = super.init(context, data);
> > +         cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > +        return obj;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#
> jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor,
> java.lang.Object)
> >       */
> >      public Object jjtAccept(ParserVisitor visitor, Object data)
> > @@ -92,16 +104,4 @@ public class ASTElseIfStatement extends
> >      {
> >          return jjtGetChild(1).render( context, writer );
> >      }
> > -
> > -    /**
> > -     * @throws TemplateInitException
> > -     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > -     */
> > -    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > -    {
> > -       Object obj = super.init(context, data);
> > -       cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > -       return obj;
> > -    }
> > -
> >  }
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTIfStatement.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTIfStatement.java?rev=1758416&r1=1758415&r2=1758416&
> view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTIfStatement.java
> (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTIfStatement.java Tue Aug
> 30 16:18:33 2016
> > @@ -34,6 +34,7 @@ import org.apache.velocity.exception.Met
> >  import org.apache.velocity.exception.ParseErrorException;
> >  import org.apache.velocity.exception.ResourceNotFoundException;
> >  import org.apache.velocity.exception.TemplateInitException;
> > +import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
> >  import org.apache.velocity.runtime.parser.Parser;
> >
> >  import java.io.IOException;
> > @@ -45,6 +46,9 @@ import java.io.Writer;
> >   */
> >  public class ASTIfStatement extends SimpleNode
> >  {
> > +    private String prefix = "";
> > +    private String postfix = "";
> > +
> >      /**
> >       * @param id
> >       */
> > @@ -62,7 +66,6 @@ public class ASTIfStatement extends Simp
> >          super(p, id);
> >      }
> >
> > -
> >      /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#
> jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor,
> java.lang.Object)
> >       */
> > @@ -72,12 +75,73 @@ public class ASTIfStatement extends Simp
> >      }
> >
> >      /**
> > +     * @throws TemplateInitException
> > +     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > +     */
> > +    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > +    {
> > +        Object obj = super.init(context, data);
> > +
> > +        /* handle structured space gobbling */
> > +        if (rsvc.getSpaceGobbling() == SpaceGobbling.STRUCTURED &&
> postfix.length() > 0)
> > +        {
> > +            NodeUtils.fixIndentation(this, prefix);
> > +        }
> > +
> > +        cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > +        return obj;
> > +    }
> > +
> > +    /**
> > +     * set indentation prefix
> > +     * @param prefix
> > +     */
> > +    public void setPrefix(String prefix)
> > +    {
> > +        this.prefix = prefix;
> > +    }
> > +
> > +    /**
> > +     * get indentation prefix
> > +     * @return prefix
> > +     */
> > +    public String getPrefix()
> > +    {
> > +        return prefix;
> > +    }
> > +
> > +    /**
> > +     * set indentation postfix
> > +     * @param postfix
> > +     */
> > +    public void setPostfix(String postfix)
> > +    {
> > +        this.postfix = postfix;
> > +    }
> > +
> > +    /**
> > +     * get indentation postfix
> > +     * @return postfix
> > +     */
> > +    public String getPostfix()
> > +    {
> > +        return postfix;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#render(
> org.apache.velocity.context.InternalContextAdapter, java.io.Writer)
> >       */
> >      public boolean render( InternalContextAdapter context, Writer
> writer)
> >          throws IOException,MethodInvocationException,
> >                 ResourceNotFoundException, ParseErrorException
> >      {
> > +        SpaceGobbling spaceGobbling = rsvc.getSpaceGobbling();
> > +
> > +        if (spaceGobbling.compareTo(SpaceGobbling.LINES) < 0)
> > +        {
> > +            writer.write(prefix);
> > +        }
> > +
> >          /*
> >           * Check if the #if(expression) construct evaluates to true:
> >           * if so render and leave immediately because there
> > @@ -105,15 +169,20 @@ public class ASTIfStatement extends Simp
> >              if (jjtGetChild(i).evaluate(context))
> >              {
> >                  jjtGetChild(i).render(context, writer);
> > -                return true;
> > +                break;
> >              }
> >          }
> >
> > +        if (spaceGobbling == SpaceGobbling.NONE)
> > +        {
> > +            writer.write(postfix);
> > +        }
> > +
> >          /*
> > -         * This is reached when an ASTIfStatement
> > -         * consists of an if/elseif sequence where
> > -         * none of the nodes evaluate to true.
> > +         * This is reached without rendering anything when an
> ASTIfStatement
> > +         * consists of an if/elseif sequence where none of the nodes
> evaluate to true.
> >           */
> > +
> >          return true;
> >      }
> >
> > @@ -124,15 +193,4 @@ public class ASTIfStatement extends Simp
> >      public void process( InternalContextAdapter context, ParserVisitor
> visitor)
> >      {
> >      }
> > -
> > -    /**
> > -     * @throws TemplateInitException
> > -     * @see org.apache.velocity.runtime.parser.node.Node#init(org.
> apache.velocity.context.InternalContextAdapter, java.lang.Object)
> > -     */
> > -    public Object init( InternalContextAdapter context, Object data)
> throws TemplateInitException
> > -    {
> > -       Object obj = super.init(context, data);
> > -       cleanupParserAndTokens(); // drop reference to Parser and all
> JavaCC Tokens
> > -       return obj;
> > -    }
> >  }
> > \ No newline at end of file
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTSetDirective.java?rev=
> 1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java
> (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTSetDirective.java Tue Aug
> 30 16:18:33 2016
> > @@ -24,6 +24,7 @@ import org.apache.velocity.context.Inter
> >  import org.apache.velocity.exception.MethodInvocationException;
> >  import org.apache.velocity.exception.TemplateInitException;
> >  import org.apache.velocity.runtime.RuntimeConstants;
> > +import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
> >  import org.apache.velocity.runtime.parser.Parser;
> >  import org.apache.velocity.util.introspection.Info;
> >
> > @@ -43,6 +44,8 @@ public class ASTSetDirective extends Sim
> >      private Node right = null;
> >      private ASTReference left = null;
> >      private boolean isInitialized;
> > +    private String prefix = "";
> > +    private String postfix = "";
> >
> >      /**
> >       *  This is really immutable after the init, so keep one for this
> node
> > @@ -111,7 +114,29 @@ public class ASTSetDirective extends Sim
> >               *  grab this now.  No need to redo each time
> >               */
> >              leftReference = left.firstImage.substring(1);
> > -
> > +
> > +            /* handle backward compatible space gobbling if asked so */
> > +            if (rsvc.getSpaceGobbling() == SpaceGobbling.BC)
> > +            {
> > +                Node previousNode = null;
> > +                for (int brother = 0; brother <
> parent.jjtGetNumChildren(); ++brother)
> > +                {
> > +                    Node node = parent.jjtGetChild(brother);
> > +                    if (node == this) break;
> > +                    previousNode = node;
> > +                }
> > +                if (previousNode == null) prefix = "";
> > +                else if (previousNode instanceof ASTText)
> > +                {
> > +                    ASTText text = (ASTText)previousNode;
> > +                    if (text.getCtext().matches("[ \t]*"))
> > +                    {
> > +                        text.setCtext("");
> > +                    }
> > +                }
> > +                else prefix = "";
> > +            }
> > +
> >              isInitialized = true;
> >
> >              cleanupParserAndTokens();
> > @@ -121,6 +146,42 @@ public class ASTSetDirective extends Sim
> >      }
> >
> >      /**
> > +     * set indentation prefix
> > +     * @param prefix
> > +     */
> > +    public void setPrefix(String prefix)
> > +    {
> > +        this.prefix = prefix;
> > +    }
> > +
> > +    /**
> > +     * get indentation prefix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPrefix()
> > +    {
> > +        return prefix;
> > +    }
> > +
> > +    /**
> > +     * set indentation postfix
> > +     * @param postfix
> > +     */
> > +    public void setPostfix(String postfix)
> > +    {
> > +        this.postfix = postfix;
> > +    }
> > +
> > +    /**
> > +     * get indentation postfix
> > +     * @return indentation prefix
> > +     */
> > +    public String getPostfix()
> > +    {
> > +        return postfix;
> > +    }
> > +
> > +    /**
> >       *   puts the value of the RHS into the context under the key of
> the LHS
> >       * @param context
> >       * @param writer
> > @@ -131,6 +192,18 @@ public class ASTSetDirective extends Sim
> >      public boolean render( InternalContextAdapter context, Writer
> writer)
> >          throws IOException, MethodInvocationException
> >      {
> > +        SpaceGobbling spaceGobbling = rsvc.getSpaceGobbling();
> > +
> > +        /* Velocity 1.x space gobbling for #set is rather wacky:
> > +           prefix is eaten *only* if previous token is not a text node.
> > +           We handle this by appropriately emptying the prefix in BC
> mode.
> > +         */
> > +
> > +        if (spaceGobbling.compareTo(SpaceGobbling.LINES) < 0)
> > +        {
> > +            writer.write(prefix);
> > +        }
> > +
> >          /*
> >           *  get the RHS node, and its value
> >           */
> > @@ -146,7 +219,13 @@ public class ASTSetDirective extends Sim
> >              }
> >              EventHandlerUtil.invalidSetMethod(rsvc, context,
> leftReference, rightReference, uberInfo);
> >          }
> > -
> > +
> > +        if (spaceGobbling == SpaceGobbling.NONE)
> > +        {
> > +            writer.write(postfix);
> > +        }
> > +
> > +
> >          return left.setValue(context, value);
> >      }
> >
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTText.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ASTText.java?rev=1758416&r1=1758415&
> r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTText.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ASTText.java Tue Aug 30
> 16:18:33 2016
> > @@ -32,7 +32,7 @@ import java.io.Writer;
> >   */
> >  public class ASTText extends SimpleNode
> >  {
> > -    private char[] ctext;
> > +    private String ctext;
> >
> >      /**
> >       * @param id
> > @@ -52,6 +52,24 @@ public class ASTText extends SimpleNode
> >      }
> >
> >      /**
> > +     * text getter
> > +     * @return ctext
> > +     */
> > +    public String getCtext()
> > +    {
> > +        return ctext;
> > +    }
> > +
> > +    /**
> > +     * text setter
> > +     * @param ctext
> > +     */
> > +    public void setCtext(String ctext)
> > +    {
> > +        this.ctext = ctext;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.SimpleNode#
> jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor,
> java.lang.Object)
> >       */
> >      public Object jjtAccept(ParserVisitor visitor, Object data)
> > @@ -65,11 +83,14 @@ public class ASTText extends SimpleNode
> >      public Object init( InternalContextAdapter context, Object data)
> >      throws TemplateInitException
> >      {
> > +        StringBuilder builder = new StringBuilder();
> >          Token t = getFirstToken();
> > -
> > -        String text = NodeUtils.tokenLiteral( t );
> > -
> > -        ctext = text.toCharArray();
> > +        for (; t != getLastToken(); t = t.next)
> > +        {
> > +            builder.append(NodeUtils.tokenLiteral(t));
> > +        }
> > +        builder.append(NodeUtils.tokenLiteral(t));
> > +        ctext = builder.toString();
> >
> >          cleanupParserAndTokens();
> >
> >
> > Added: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/IndentationFixer.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/IndentationFixer.java?rev=1758416&view=auto
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/IndentationFixer.java (added)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/IndentationFixer.java Tue
> Aug 30 16:18:33 2016
> > @@ -0,0 +1,362 @@
> > +package org.apache.velocity.runtime.parser.node;
> > +
> > +import org.apache.velocity.runtime.directive.Directive;
> > +
> > +import java.util.regex.Matcher;
> > +import java.util.regex.Pattern;
> > +
> > +/**
> > + * Helper class to fix indentation in structured mode.
> > + */
> > +
> > +public class IndentationFixer implements ParserVisitor
> > +{
> > +    protected String parentIndentation = null;
> > +    protected String extraIndentation = null;
> > +    protected Pattern fix = null;
> > +
> > +    protected void fillExtraIndentation(String prefix)
> > +    {
> > +        Pattern captureExtraIndentation = Pattern.compile("^" +
> parentIndentation + "(\\s+)");
> > +        Matcher matcher = captureExtraIndentation.matcher(prefix);
> > +        if (matcher.find())
> > +        {
> > +            extraIndentation = matcher.group(1);
> > +            fix = Pattern.compile("^" + parentIndentation +
> extraIndentation, Pattern.MULTILINE);
> > +        }
> > +        else
> > +        {
> > +            extraIndentation = "";
> > +        }
> > +    }
> > +
> > +    public IndentationFixer(String parentIndentation)
> > +    {
> > +        this.parentIndentation = parentIndentation;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(SimpleNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTprocess node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTText node, Object data)
> > +    {
> > +        String text = node.getCtext();
> > +        if (extraIndentation == null)
> > +        {
> > +            fillExtraIndentation(text);
> > +        }
> > +        if (extraIndentation.length() > 0)
> > +        {
> > +            Matcher matcher = fix.matcher(text);
> > +            node.setCtext(matcher.replaceAll(parentIndentation));
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTEscapedDirective node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTEscape node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTComment node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTTextblock node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTFloatingPointLiteral node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTIntegerLiteral node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTStringLiteral node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTIdentifier node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTWord node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTDirectiveAssign node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTDirective node, Object data)
> > +    {
> > +        String prefix = node.getPrefix();
> > +        if (prefix.length() > 0)
> > +        {
> > +            if (extraIndentation == null)
> > +            {
> > +                fillExtraIndentation(prefix);
> > +            }
> > +            if (extraIndentation.length() > 0)
> > +            {
> > +                Matcher matcher = fix.matcher(prefix);
> > +                node.setPrefix(matcher.replaceAll(parentIndentation));
> > +                if (node.getDirectiveType() == Directive.BLOCK)
> > +                {
> > +                    node.childrenAccept(this, null);
> > +                }
> > +            }
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTBlock node, Object data)
> > +    {
> > +        String prefix = node.getPrefix();
> > +        if (prefix.length() > 0)
> > +        {
> > +            node.childrenAccept(this, null);
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTMap node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTObjectArray node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTIntegerRange node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTMethod node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTIndex node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTReference node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTTrue node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTFalse node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTIfStatement node, Object data)
> > +    {
> > +        String prefix = node.getPrefix();
> > +        if (prefix.length() > 0)
> > +        {
> > +            if (extraIndentation == null)
> > +            {
> > +                fillExtraIndentation(prefix);
> > +            }
> > +            if (extraIndentation.length() > 0)
> > +            {
> > +                Matcher matcher = fix.matcher(prefix);
> > +                node.setPrefix(matcher.replaceAll(parentIndentation));
> > +                node.childrenAccept(this, null);
> > +            }
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTElseStatement node, Object data)
> > +    {
> > +        if (extraIndentation != null && extraIndentation.length() > 0)
> > +        {
> > +            node.childrenAccept(this, null);
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTElseIfStatement node, Object data)
> > +    {
> > +        if (extraIndentation != null && extraIndentation.length() > 0)
> > +        {
> > +            node.childrenAccept(this, null);
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTSetDirective node, Object data)
> > +    {
> > +        String prefix = node.getPrefix();
> > +        if (prefix.length() > 0)
> > +        {
> > +            if (extraIndentation == null)
> > +            {
> > +                fillExtraIndentation(prefix);
> > +            }
> > +            if (extraIndentation.length() > 0)
> > +            {
> > +                Matcher matcher = fix.matcher(prefix);
> > +                node.setPrefix(matcher.replaceAll(parentIndentation));
> > +            }
> > +        }
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTExpression node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTAssignment node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTOrNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTAndNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTEQNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTNENode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTLTNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTGTNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTLENode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTGENode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTAddNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTSubtractNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTMulNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTDivNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTModNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +
> > +    @Override
> > +    public Object visit(ASTNotNode node, Object data)
> > +    {
> > +        return null;
> > +    }
> > +}
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/NodeUtils.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/NodeUtils.java?rev=1758416&r1=1758415&
> r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/NodeUtils.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/NodeUtils.java Tue Aug 30
> 16:18:33 2016
> > @@ -147,4 +147,17 @@ public class NodeUtils
> >              return t.image;
> >          }
> >      }
> > +
> > +    /**
> > +     * Fix children indentation in structured space gobbling mode.
> > +     * @param parent
> > +     * @param parentIndentation
> > +     * @param extraIndentation
> > +     * @return
> > +     */
> > +    public static void fixIndentation(SimpleNode parent, String
> parentIndentation)
> > +    {
> > +        IndentationFixer fixer = new IndentationFixer(
> parentIndentation);
> > +        parent.childrenAccept(fixer, null);
> > +    }
> >  }
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ParserVisitor.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/parser/node/ParserVisitor.java?rev=1758416&r1=1758415&r2=1758416&
> view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ParserVisitor.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/parser/node/ParserVisitor.java Tue Aug
> 30 16:18:33 2016
> > @@ -41,6 +41,7 @@ public interface ParserVisitor
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > +
> >    public Object visit(ASTprocess node, Object data);
> >
> >    /**
> > @@ -48,6 +49,13 @@ public interface ParserVisitor
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > +  public Object visit(ASTText node, Object data);
> > +
> > +  /**
> > +   * @param node
> > +   * @param data
> > +   * @return The object rendered by this node.
> > +   */
> >    public Object visit(ASTEscapedDirective node, Object data);
> >
> >    /**
> > @@ -63,6 +71,12 @@ public interface ParserVisitor
> >     * @return The object rendered by this node.
> >     */
> >    public Object visit(ASTComment node, Object data);
> > +  /**
> > +   * @param node
> > +   * @param data
> > +   * @return The object rendered by this node.
> > +   */
> > +  public Object visit(ASTTextblock node, Object data);
> >
> >    /**
> >     * @param node
> > @@ -104,6 +118,13 @@ public interface ParserVisitor
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > +
> > +  public Object visit(ASTDirectiveAssign node, Object data);
> > +  /**
> > +   * @param node
> > +   * @param data
> > +   * @return The object rendered by this node.
> > +   */
> >    public Object visit(ASTDirective node, Object data);
> >
> >    /**
> > @@ -146,28 +167,28 @@ public interface ParserVisitor
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > -  public Object visit(ASTReference node, Object data);
> > +  public Object visit(ASTIndex node, Object data);
> >
> >    /**
> >     * @param node
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > -  public Object visit(ASTTrue node, Object data);
> > +  public Object visit(ASTReference node, Object data);
> >
> >    /**
> >     * @param node
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > -  public Object visit(ASTFalse node, Object data);
> > +  public Object visit(ASTTrue node, Object data);
> >
> >    /**
> >     * @param node
> >     * @param data
> >     * @return The object rendered by this node.
> >     */
> > -  public Object visit(ASTText node, Object data);
> > +  public Object visit(ASTFalse node, Object data);
> >
> >    /**
> >     * @param node
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/visitor/BaseVisitor.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/java/org/apache/velocity/
> runtime/visitor/BaseVisitor.java?rev=1758416&r1=1758415&
> r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/visitor/BaseVisitor.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> java/org/apache/velocity/runtime/visitor/BaseVisitor.java Tue Aug 30
> 16:18:33 2016
> > @@ -266,6 +266,15 @@ public abstract class BaseVisitor implem
> >      }
> >
> >      /**
> > +     * @see org.apache.velocity.runtime.parser.node.ParserVisitor#
> visit(org.apache.velocity.runtime.parser.node.ASTIndex, java.lang.Object)
> > +     */
> > +    public Object visit(ASTIndex node, Object data)
> > +    {
> > +        data = node.childrenAccept(this, data);
> > +        return data;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.ParserVisitor#
> visit(org.apache.velocity.runtime.parser.node.ASTReference,
> java.lang.Object)
> >       */
> >      public Object visit(ASTReference node, Object data)
> > @@ -347,6 +356,15 @@ public abstract class BaseVisitor implem
> >      }
> >
> >      /**
> > +     * @see org.apache.velocity.runtime.parser.node.ParserVisitor#
> visit(org.apache.velocity.runtime.parser.node.ASTTextblock,
> java.lang.Object)
> > +     */
> > +    public Object visit(ASTTextblock node, Object data)
> > +    {
> > +        data = node.childrenAccept(this, data);
> > +        return data;
> > +    }
> > +
> > +    /**
> >       * @see org.apache.velocity.runtime.parser.node.ParserVisitor#
> visit(org.apache.velocity.runtime.parser.node.ASTObjectArray,
> java.lang.Object)
> >       */
> >      public Object visit(ASTObjectArray node, Object data)
> > @@ -371,6 +389,15 @@ public abstract class BaseVisitor implem
> >      {
> >          data = node.childrenAccept(this, data);
> >          return data;
> > +    }
> > +
> > +    /**
> > +     * @see org.apache.velocity.runtime.parser.node.ParserVisitor#
> visit(org.apache.velocity.runtime.parser.node.ASTDirectiveAssign,
> java.lang.Object)
> > +     */
> > +    public Object visit(ASTDirectiveAssign node, Object data)
> > +    {
> > +        data = node.childrenAccept(this, data);
> > +        return data;
> >      }
> >
> >      /**
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> parser/Parser.jjt
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/parser/Parser.jjt?rev=1758416&
> r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/parser/Parser.jjt
> (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/parser/Parser.jjt
> Tue Aug 30 16:18:33 2016
> > @@ -70,8 +70,9 @@ options
> >      /**
> >       *  for debugging purposes.  Keep false
> >       */
> > -    DEBUG_PARSER=false;
> > -    DEBUG_TOKEN_MANAGER=false;
> > +    DEBUG_PARSER = false;
> > +    DEBUG_LOOKAHEAD = false;
> > +    DEBUG_TOKEN_MANAGER = false;
> >  }
> >
> >  PARSER_BEGIN(Parser)
> > @@ -393,7 +394,6 @@ TOKEN_MGR_DECLS:
> >      public boolean debugPrint = false;
> >
> >      private boolean inReference;
> > -    public boolean inDirective;
> >      private boolean inComment;
> >      public  boolean inSet;
> >
> > @@ -468,7 +468,6 @@ TOKEN_MGR_DECLS:
> >          lparen = 0;
> >          rparen = 0;
> >          inReference = false;
> > -        inDirective = false;
> >          inComment = false;
> >          inSet = false;
> >
> > @@ -544,13 +543,9 @@ TOKEN_MGR_DECLS:
> >   *
> >   * Tokens
> >   *
> > - *  Note : we now have another state, REFMODIFIER.  This is sort of a
> > - *  type of REFERENCE state, simply use to use the DIRECTIVE token
> > - *  set when we are processing a $foo.bar() construct
> > - *
> >   * 
> > -------------------------------------------------------------------------
> */
> >
> > -<REFERENCE, REFMODIFIER>
> > +<REFERENCE, REFMODIFIER, REFMOD3>
> >  TOKEN:
> >  {
> >     <INDEX_LBRACKET: "[">
> > @@ -623,10 +618,7 @@ TOKEN:
> >  <DIRECTIVE>
> >  TOKEN:
> >  {
> > -    /*
> > -     *  We will eat any whitespace upto and including a newline for
> directives
> > -     */
> > -    <RPAREN: ")" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ))?>
> > +    <RPAREN: ")">
> >      {
> >         RPARENHandler();
> >      }
> > @@ -648,7 +640,7 @@ TOKEN:
> >           * $foo.bar().blargh().woogie().doogie()
> >           */
> >
> > -        SwitchTo( REFERENCE );
> > +        SwitchTo( REFMOD3 );
> >      }
> >  }
> >
> > @@ -676,11 +668,6 @@ TOKEN:
> >
> >
> >  /*
> > - * needed because #set is so wacky in it's desired behavior.  We want
> set
> > - * to eat any preceeding whitespace so it is invisible in formatting.
> > - * (As it should be.)  If this works well, I am going to chuck the
> whole MORE:
> > - * token abomination.
> > - *
> >   * We added the lexical states REFERENCE, REFMODIFIER, REFMOD2 to
> >   * address JIRA issue VELOCITY-631. With SET_DIRECTIVE only in the
> >   * DEFAULT lexical state the following VTL fails "$a#set($b = 1)"
> > @@ -695,12 +682,10 @@ TOKEN:
> >  <DEFAULT, REFERENCE, REFMODIFIER, REFMOD2>
> >  TOKEN:
> >  {
> > -  <SET_DIRECTIVE: (" "|"\t")*  ("#set" | "#{set}")  (" "|"\t")* "(">
> > +  <SET_DIRECTIVE: ("#set" | "#{set}")  (" "|"\t")* "(">
> >      {
> >          if (! inComment)
> >          {
> > -            inDirective = true;
> > -
> >              if ( debugPrint )
> >                  System.out.print("#set :  going to " + DIRECTIVE );
> >
> > @@ -833,8 +818,6 @@ MORE :
> >                  stateStackPop();
> >              }
> >
> > -            inDirective = true;
> > -
> >              if ( debugPrint )
> >                  System.out.print("# :  going to " + DIRECTIVE );
> >
> > @@ -844,7 +827,6 @@ MORE :
> >      }
> >  }
> >
> > -
> >  // treat the single line comment case separately
> >  // to avoid ##<EOF> errors
> >  <DEFAULT,PRE_DIRECTIVE,DIRECTIVE,REFERENCE>
> > @@ -867,14 +849,6 @@ TOKEN :
> >       }
> >  }
> >
> > -TOKEN :
> > -{
> > -    <DOUBLE_ESCAPE : "\\\\">
> > -|   <ESCAPE: "\\" >
> > -|   <TEXT: (~["$", "#", "\\"])+ >
> > -}
> > -
> > -
> >  /* ------------------------------------------------------------
> -----------
> >   *
> >   *   *_COMMENT Lexical tokens
> > @@ -939,10 +913,20 @@ MORE :
> >   *
> >   * ----------------------------------------------------------------------
> */
> >
> > -<DIRECTIVE,REFMOD2,REFINDEX>
> > +<DEFAULT,REFINDEX,REFMOD2,DIRECTIVE>
> >  TOKEN:
> >  {
> > -    <WHITESPACE : ([" ","\t", "\n", "\r"])+ >
> > +    <WHITESPACE : ([" ","\t"])+>
> > +|   <NEWLINE : ("\n" | "\r" | "\r\n") >
> > +    {
> > +        if ( debugPrint )
> > +            System.out.println(" NEWLINE :");
> > +
> > +//        stateStackPop();
> > +
> > +        if (inSet)
> > +            inSet = false;
> > +    }
> >  }
> >
> >  <DIRECTIVE,REFMOD2,REFINDEX>
> > @@ -993,25 +977,6 @@ TOKEN:
> >  |   <FALSE: "false">
> >  }
> >
> > -<DIRECTIVE>
> > -TOKEN :
> > -{
> > -    <NEWLINE: "\n" | "\r" | "\r\n" >
> > -    {
> > -        if ( debugPrint )
> > -            System.out.println(" NEWLINE :");
> > -
> > -        stateStackPop();
> > -
> > -        if (inSet)
> > -            inSet = false;
> > -
> > -        if (inDirective)
> > -            inDirective = false;
> > -    }
> > -}
> > -
> > -
> >  <DIRECTIVE,REFMOD2>
> >  TOKEN :
> >  {
> > @@ -1035,10 +1000,8 @@ TOKEN :
> >  <PRE_DIRECTIVE>
> >  TOKEN :
> >  {
> > -    <END: ( "end" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )
> > -          | ("{end}" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? ) >
> > +    <END: ( "end" | "{end}" )>
> >      {
> > -        inDirective = false;
> >          stateStackPop();
> >      }
> >
> > @@ -1047,16 +1010,13 @@ TOKEN :
> >          SwitchTo(DIRECTIVE);
> >      }
> >
> > -|   <ELSEIF_DIRECTIVE: "elseif" | "{elseif}">
> > +|   <ELSEIF: "elseif" | "{elseif}">
> >      {
> >          SwitchTo(DIRECTIVE);
> >      }
> >
> > -|   <ELSE_DIRECTIVE:
> > -          ( "else" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )
> > -        | ( "{else}" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )  >
> > -     {
> > -        inDirective = false;
> > +|   <ELSE: "else" | "{else}">
> > +    {
> >          stateStackPop();
> >      }
> >  }
> > @@ -1145,17 +1105,17 @@ TOKEN:
> >   *  for each state can be different.
> >   *
> >   *  $foo.bar( "arg" )
> > - *  ^   ^   ^
> > - *  |   |   |
> > - *  ----------- >  REFERENCE : state initiated by the '$' character.
> Continues
> > - *      |   |       until end of the reference, or the . character.
> > - *      |------ >  REFMODIFIER : state switched to when the <DOT> is
> encountered.
> > - *          |       note that this is a switch, not a push. See notes
> at bottom
> > - *          |       re stateStack.
> > - *          |-- >  REFMOD2 : state switch to when the LPAREN is
> encountered.
> > - *                  again, this is a switch, not a push.
> > + *  ^   ^   ^       ^
> > + *  |   |   |       |
> > + *  |_________________ >  REFERENCE : state initiated by the '$'
> character.  Continues
> > + *      |   |       |     until end of the reference, or the .
> character.
> > + *      |_____________ >  REFMODIFIER : state switched to when the
> <DOT> is encountered.
> > + *          |       |     note that this is a switch, not a push. See
> notes at bottom.
> > + *          |_________ >  REFMOD2 : state switch to when the LPAREN is
> encountered.
> > + *                  |     again, this is a switch, not a push.
> > + *                  |_ >  REFMOD3 : state only checking for a possible
> '.' or '['  continuation.
> >   *
> > - *  During the REFERENCE or REFMODIFIER lex states we will switch to
> > + *  During the REFERENCE, REFMODIFIER or REFMOD3 lex states we will
> switch to
> >   *  REFINDEX if a bracket is encountered '['.  for example:  $foo[1]
> >   *  or $foo.bar[1], $foo.bar( "arg" )[1]
> >   * 
> > ----------------------------------------------------------------------------
> */
> > @@ -1166,7 +1126,12 @@ TOKEN :
> >      <#ALPHA_CHAR: ["a"-"z", "A"-"Z", "_"] >
> >  |   <#IDENTIFIER_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "_" ] >
> >  |   <IDENTIFIER:  ( <ALPHA_CHAR> ) (<IDENTIFIER_CHAR>)* >
> > -|   <DOT: "." <ALPHA_CHAR>>
> > +}
> > +
> > +<REFERENCE,REFMODIFIER,REFMOD2,REFMOD3>
> > +TOKEN:
> > +{
> > +   <DOT: "." <ALPHA_CHAR>>
> >      {
> >          /*
> >           * push the alpha char back into the stream so the following
> identifier
> > @@ -1189,8 +1154,7 @@ TOKEN :
> >      }
> >  }
> >
> > -
> > -<REFERENCE,REFMODIFIER>
> > +<REFERENCE,REFMODIFIER,REFMOD3>
> >  TOKEN :
> >  {
> >      <LCURLY: "{">
> > @@ -1200,7 +1164,7 @@ TOKEN :
> >      }
> >  }
> >
> > -<REFERENCE,REFMODIFIER,REFMOD>
> > +<REFERENCE,REFMODIFIER,REFMOD,REFMOD3>
> >  SPECIAL_TOKEN :
> >  {
> >      <REFERENCE_TERMINATOR: ~[] >
> > @@ -1229,11 +1193,25 @@ SPECIAL_TOKEN :
> >              System.out.print("DIRECTIVE_TERM :");
> >
> >          input_stream.backup(1);
> > -        inDirective = false;
> >          stateStackPop();
> >      }
> >  }
> >
> > +/* TEXT must end with a newline, and contain at least one
> non-whitespace character in the first line,
> > +   so that the <WHITESPACE> <NEWLINE> sequence is not read as a TEXT
> (needed for space gobbling)
> > +*/
> > +TOKEN :
> > +{
> > +    <DOUBLE_ESCAPE : "\\\\">
> > +|   <ESCAPE: "\\" >
> > +|   <TEXT: (~["$", "#", "\\", "\r", "\n"])* (~["$", "#", "\\", "\r",
> "\n", " ", "\t"])+ (~["$", "#", "\\", "\r", "\n"])* <NEWLINE> ((~["$", "#",
> "\\", "\r", "\n"])* <NEWLINE>)* >
> > +}
> > +
> > +TOKEN :
> > +{
> > +    <INLINE_TEXT: (~["$", "#", "\\", "\r", "\n"])+ >
> > +}
> > +
> >  /**
> >   * This method is what starts the whole parsing
> >   * process. After the parsing is complete and
> > @@ -1243,9 +1221,12 @@ SPECIAL_TOKEN :
> >   * which implements the ParserVisitor interface
> >   * which is generated automatically by JavaCC
> >   */
> > -SimpleNode process() : {}
> > +SimpleNode process() :
> > +{
> > +    boolean afterNewline = true;
> > +}
> >  {
> > -   ( Statement() )* <EOF>
> > +   ( LOOKAHEAD({ getToken(1).kind != EOF }) afterNewline =
> Statement(afterNewline) )* <EOF>
> >     { return jjtThis; }
> >  }
> >
> > @@ -1253,17 +1234,23 @@ SimpleNode process() : {}
> >   * These are the types of statements that
> >   * are acceptable in Velocity templates.
> >   */
> > -void Statement() #void : {}
> > +boolean Statement(boolean afterNewline) #void :
> >  {
> > -    IfStatement()
> > -|   LOOKAHEAD(2) Reference()
> > -|   Comment()
> > -|   Textblock()
> > -|   SetDirective()
> > -|   EscapedDirective()
> > -|   Escape()
> > -|   Directive()
> > -|   Text()
> > +    boolean b = false;
> > +}
> > +{
> > +    LOOKAHEAD( { getToken(1).kind == IF_DIRECTIVE || afterNewline &&
> getToken(1).kind == WHITESPACE && getToken(2).kind == IF_DIRECTIVE } ) b =
> IfStatement() { return b ; }
> > +|   LOOKAHEAD(2) Reference() { return false; }
> > +|   LOOKAHEAD(2) Comment() { return false; }
> > +|   Textblock() { return false; }
> > +|   LOOKAHEAD( { getToken(1).kind == SET_DIRECTIVE || afterNewline &&
> getToken(1).kind == WHITESPACE && getToken(2).kind == SET_DIRECTIVE } ) b =
> SetDirective() { return b; }
> > +|   EscapedDirective() { return false; }
> > +|   Escape() { return false; }
> > +|   LOOKAHEAD( { getToken(1).kind == WORD || getToken(1).kind ==
> BRACKETED_WORD || afterNewline && getToken(1).kind == WHITESPACE && (
> getToken(2).kind == WORD || getToken(2).kind == BRACKETED_WORD ) } ) b =
> Directive() { return b; }
> > +|   b = Text() { return b; }
> > +|   (<NEWLINE>) #Text { return true; }
> > +|   (<INLINE_TEXT> ((<TEXT>)? { b = true; }) ) #Text { return b; }
> > +|   (<WHITESPACE>) #Text { return false; }
> >  }
> >
> >  /**
> > @@ -1314,8 +1301,8 @@ void Escape() : {}
> >           */
> >          switch(t.next.kind ) {
> >              case IF_DIRECTIVE :
> > -            case ELSE_DIRECTIVE :
> > -            case ELSEIF_DIRECTIVE :
> > +            case ELSE :
> > +            case ELSEIF :
> >              case END :
> >                  control = true;
> >                  break;
> > @@ -1414,7 +1401,7 @@ int DirectiveArg() #void : {}
> >      /*
> >       * Need to put this before the floating point expansion
> >       */
> > -|   LOOKAHEAD(  <LBRACKET> [<WHITESPACE>] ( Reference() |
> IntegerLiteral())     [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange()
> > +|   LOOKAHEAD(  <LBRACKET> (<WHITESPACE> | <NEWLINE>)* ( Reference() |
> IntegerLiteral())     (<WHITESPACE> | <NEWLINE>)* <DOUBLEDOT> )
> IntegerRange()
> >      {
> >          return ParserTreeConstants.JJTINTEGERRANGE;
> >      }
> > @@ -1449,10 +1436,11 @@ void DirectiveAssign() : {}
> >  /**
> >   *   Supports the Pluggable Directives
> >   *     #foo( arg+ )
> > + * @returns true if ends with a newline
> >   */
> > -SimpleNode Directive() :
> > +boolean Directive() :
> >  {
> > -    Token t = null;
> > +    Token id = null, t = null, u = null;
> >      int argType;
> >      int argPos = 0;
> >      Directive d;
> > @@ -1460,23 +1448,32 @@ SimpleNode Directive() :
> >      boolean isVM = false;
> >      boolean isMacro = false;
> >      ArrayList argtypes = new ArrayList(4);
> > +    boolean newlineAtEnd = false, newlineBeforeStatement = false;
> > +    String blockPrefix = "";
> > +    ASTBlock block = null;
> >  }
> >  {
> > -
> > +    [
> > +      (t = <WHITESPACE>)
> > +      {
> > +          jjtThis.setPrefix(t.image);
> > +          t = null;
> > +      }
> > +    ]
> >      /*
> >       * note that if we were escaped, that is now handled by
> >       * EscapedDirective()
> >       */
> > -    ((t = <WORD>) | (t = <BRACKETED_WORD>))
> > +    ((id = <WORD>) | (id = <BRACKETED_WORD>))
> >      {
> >          String directiveName;
> > -        if (t.kind == ParserConstants.BRACKETED_WORD)
> > +        if (id.kind == ParserConstants.BRACKETED_WORD)
> >          {
> > -            directiveName = t.image.substring(2, t.image.length() - 1);
> > +            directiveName = id.image.substring(2, id.image.length() -
> 1);
> >          }
> >          else
> >          {
> > -            directiveName = t.image.substring(1);
> > +            directiveName = id.image.substring(1);
> >          }
> >
> >          d = getDirective(directiveName);
> > @@ -1526,7 +1523,6 @@ SimpleNode Directive() :
> >           */
> >
> >          token_source.SwitchTo(DIRECTIVE);
> > -
> >          argPos = 0;
> >      }
> >
> > @@ -1538,14 +1534,16 @@ SimpleNode Directive() :
> >      /*
> >       *  if this is indeed a token, match the #foo ( arg ) pattern
> >       */
> > -    ([<WHITESPACE>] <LPAREN> ( LOOKAHEAD(2) [<WHITESPACE>] [<COMMA>
> [<WHITESPACE>]]
> > +    ((<WHITESPACE> | <NEWLINE>)* <LPAREN> ( LOOKAHEAD(2) (<WHITESPACE>
> | <NEWLINE>)* [<COMMA> (<WHITESPACE> | <NEWLINE>)*]
> >         (
> >             [LOOKAHEAD( { isMacro && isAssignment() })
> > -              DirectiveAssign() [<WHITESPACE>] <EQUALS> [<WHITESPACE>]
> > +            DirectiveAssign() (<WHITESPACE> | <NEWLINE>)* <EQUALS> (
> <WHITESPACE> | <NEWLINE> )*
> >             {
> >                 argtypes.add(ParserTreeConstants.JJTDIRECTIVEASSIGN);
> >             }
> >             ]
> > +           LOOKAHEAD( { getToken(1).kind != RPAREN } )
> > +           (
> >              argType = DirectiveArg()
> >              {
> >                  argtypes.add(argType);
> > @@ -1554,48 +1552,84 @@ SimpleNode Directive() :
> >                      if (isVM)
> >                      {
> >                          throw new MacroParseException("Invalid argument
> "
> > -                        + (argPos+1) + " in macro call " + t.image,
> currentTemplate.getName(), t);
> > +                          + (argPos+1) + " in macro call " + id.image,
> currentTemplate.getName(), id);
> >                      }
> >                  }
> >
> >                  argPos++;
> >              }
> > +           )
> >          |
> >          {
> >            if (!isMacro)
> >            {
> >                // We only allow line comments in macro definitions for
> now
> > -              throw new MacroParseException("A Line comment is not
> allowed in " + t.image
> > -              + " arguments", currentTemplate.getName(), t);
> > +              throw new MacroParseException("A Line comment is not
> allowed in " + id.image
> > +                + " arguments", currentTemplate.getName(), id);
> >            }
> >          }
> >
> >           <SINGLE_LINE_COMMENT_START> [<SINGLE_LINE_COMMENT>]
> >        )
> > -    )* [<WHITESPACE>] <RPAREN>)
> > +    )* (<WHITESPACE> | <NEWLINE>)* <RPAREN>
> > +     [
> > +       LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +       {
> > +           if (directiveType == Directive.LINE)
> > +           {
> > +               jjtThis.setPostfix(t == null ? u.image : t.image +
> u.image);
> > +               newlineAtEnd = true;
> > +           }
> > +           else
> > +           {
> > +               blockPrefix = (t == null ? u.image : t.image + u.image);
> > +               newlineBeforeStatement = true;
> > +           }
> > +           t = u = null;
> > +       }
> > +     ]
> > +     )
> >      |
> >      {
> >          token_source.stateStackPop();
> > -        token_source.inDirective = false;
> >      }
> >      )
> >      {
> >          if (d != null)
> >          {
> > -            d.checkArgs(argtypes, t, currentTemplate.getName());
> > +            d.checkArgs(argtypes, id, currentTemplate.getName());
> >          }
> > -
> >          if (directiveType  == Directive.LINE)
> >          {
> > -            return jjtThis;
> > +            return newlineAtEnd;
> >          }
> >      }
> >      /*
> >       *  and the following block if the PD needs it
> >       */
> > -
> > -    ( Statement() )* #Block
> > -    <END>
> > +    (((
> > +        LOOKAHEAD( { getToken(1).kind != END && (
> !newlineBeforeStatement || getToken(1).kind != WHITESPACE ||
> getToken(2).kind != END ) }) newlineBeforeStatement = Statement(
> newlineBeforeStatement))*
> > +        {
> > +            block = jjtThis;
> > +            block.setPrefix(blockPrefix);
> > +        })
> > +        #Block)
> > +    [ LOOKAHEAD( 1, { newlineBeforeStatement })
> > +     (t = <WHITESPACE>)
> > +      {
> > +          block.setPostfix(t.image);
> > +          t = null;
> > +      }
> > +    ]
> > +    (<END>
> > +     [ LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +     {
> > +         jjtThis.setPostfix(t == null ? u.image : t.image + u.image);
> > +         t = u = null;
> > +         newlineAtEnd = true;
> > +     }
> > +     ]
> > +    )
> >      {
> >          /*
> >           *  VM : if we are processing a #macro directive, we need to
> > @@ -1615,14 +1649,14 @@ SimpleNode Directive() :
> >
> >          if (d != null)
> >          {
> > -            d.checkArgs(argtypes, t, currentTemplate.getName());
> > +            d.checkArgs(argtypes, id, currentTemplate.getName());
> >          }
> >
> >          /*
> >           *  VM : end
> >           */
> >
> > -        return jjtThis;
> > +        return newlineAtEnd;
> >      }
> >  }
> >
> > @@ -1637,7 +1671,7 @@ void Map() : {}
> >      (
> >        LOOKAHEAD(2) Parameter() <COLON> Parameter() (<COMMA> Parameter()
> <COLON> Parameter() )*
> >        |
> > -      [ <WHITESPACE> ]
> > +      ( <WHITESPACE> | <NEWLINE> )*
> >       )
> >
> >       /** note: need both tokens as they are generated in different
> states **/
> > @@ -1657,11 +1691,11 @@ void ObjectArray() : {}
> >   */
> >  void IntegerRange() : {}
> >  {
> > -    <LBRACKET> [<WHITESPACE>]
> > +    <LBRACKET> (<WHITESPACE> | <NEWLINE>)*
> >      ( Reference() | IntegerLiteral())
> > -    [<WHITESPACE>] <DOUBLEDOT> [<WHITESPACE>]
> > +    (<WHITESPACE>|<NEWLINE>)* <DOUBLEDOT> (<WHITESPACE>|<NEWLINE>)*
> >      (Reference() | IntegerLiteral())
> > -    [<WHITESPACE>] <RBRACKET>
> > +    (<WHITESPACE>|<NEWLINE>)* <RBRACKET>
> >  }
> >
> >
> > @@ -1670,7 +1704,7 @@ void IntegerRange() : {}
> >   */
> >  void IndexParameter() #void: {}
> >  {
> > -    [<WHITESPACE>]
> > +    (<WHITESPACE>|<NEWLINE>)*
> >      (
> >          StringLiteral()
> >          | IntegerLiteral()
> > @@ -1678,7 +1712,7 @@ void IndexParameter() #void: {}
> >          | False()
> >          | Reference()
> >          )
> > -    [ <WHITESPACE>]
> > +    (<WHITESPACE>|<NEWLINE>)*
> >  }
> >
> >
> > @@ -1689,11 +1723,11 @@ void IndexParameter() #void: {}
> >   */
> >  void Parameter() #void: {}
> >  {
> > -    [<WHITESPACE>]
> > +    (<WHITESPACE>|<NEWLINE>)*
> >      (
> >          StringLiteral()
> >          | IntegerLiteral()
> > -        | LOOKAHEAD(  <LBRACKET> [<WHITESPACE>]    ( Reference() |
> IntegerLiteral())     [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange()
> > +        | LOOKAHEAD(  <LBRACKET> ( <WHITESPACE> | <NEWLINE> )*    (
> Reference() | IntegerLiteral())     ( <WHITESPACE> | <NEWLINE> )*
> <DOUBLEDOT> ) IntegerRange()
> >          | Map()
> >          | ObjectArray()
> >          | True()
> > @@ -1768,20 +1802,21 @@ TOKEN :
> >   * This method is responsible for allowing
> >   * all non-grammar text to pass through
> >   * unscathed.
> > + * @returns true if last read token was a newline
> >   */
> > -void Text() : {}
> > +boolean Text() : {}
> >  {
> > -    <TEXT>
> > -|   <DOT>
> > -|   <RPAREN>
> > -|   <LPAREN>
> > -|   <INTEGER_LITERAL>
> > -|   <FLOATING_POINT_LITERAL>
> > -|   <STRING_LITERAL>
> > -|   <ESCAPE>
> > -|   <LCURLY>
> > -|   <RCURLY>
> > -|   <EMPTY_INDEX>
> > +    <TEXT> { return true; }
> > +  |  <DOT> { return false; }
> > +  |  <RPAREN> { return false; }
> > +  |  <LPAREN> { return false; }
> > +  |  <INTEGER_LITERAL> { return false; }
> > +  |  <FLOATING_POINT_LITERAL> { return false; }
> > +  |  <STRING_LITERAL> { return false; }
> > +  |  <ESCAPE> { return false; }
> > +  |  <LCURLY> { return false; }
> > +  |  <RCURLY> { return false; }
> > +  |  <EMPTY_INDEX> { return false; }
> >  }
> >
> >  /* ------------------------------------------------------------
> -----------
> > @@ -1790,26 +1825,130 @@ void Text() : {}
> >   *
> >   * ------------------------------------------------------------
> ----------*/
> >
> > -void IfStatement() : {}
> > +boolean IfStatement() :
> >  {
> > -    <IF_DIRECTIVE> [<WHITESPACE>] <LPAREN> Expression() <RPAREN>
> > -    ( Statement() )* #Block
> > -    [ LOOKAHEAD(1) ( ElseIfStatement() )+ ]
> > -    [ LOOKAHEAD(1) ElseStatement() ]
> > +    Token t = null, u = null;
> > +    ASTBlock lastBlock = null;
> > +    boolean afterNewline = false, newlineAtEnd = false;
> > +}
> > +{
> > +    [ ( t = <WHITESPACE> )
> > +        {
> > +            jjtThis.setPrefix(t.image);
> > +            t = null;
> > +        }
> > +    ]
> > +    <IF_DIRECTIVE> ( <WHITESPACE> | <NEWLINE> )* <LPAREN> Expression()
> <RPAREN>
> > +    (
> > +      [
> > +        LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +        {
> > +            jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
> > +            t = u = null;
> > +            afterNewline = true;
> > +        }
> > +      ]
> > +      ( LOOKAHEAD(
> > +        {
> > +            (getToken(1).kind != ELSEIF && getToken(1).kind != ELSE &&
> getToken(1).kind != END) &&
> > +              (!afterNewline || getToken(1).kind != WHITESPACE ||
> (getToken(2).kind != ELSEIF && getToken(2).kind != ELSE && getToken(2).kind
> != END))
> > +        })
> > +        afterNewline = Statement(afterNewline) )*
> > +        {
> > +            lastBlock = jjtThis;
> > +        }
> > +    ) #Block
> > +    [ LOOKAHEAD( { getToken(1).kind == ELSEIF || (afterNewline &&
> getToken(1).kind == WHITESPACE && getToken(2).kind == ELSEIF) })
> > +      ( LOOKAHEAD( { getToken(1).kind == ELSEIF || (afterNewline &&
> getToken(1).kind == WHITESPACE && getToken(2).kind == ELSEIF) }) (
> lastBlock = ElseIfStatement(lastBlock) { afterNewline =
> lastBlock.endsWithNewline; } ))+ ]
> > +    [ LOOKAHEAD( { getToken(1).kind == ELSE || (afterNewline &&
> getToken(1).kind == WHITESPACE && getToken(2).kind == ELSE) } ) lastBlock =
> ElseStatement(lastBlock) { afterNewline = lastBlock.endsWithNewline; } ]
> > +    [ LOOKAHEAD( 1, { afterNewline } ) ( t = <WHITESPACE> )
> > +        {
> > +            lastBlock.setPostfix(t.image);
> > +            t = null;
> > +        }
> > +    ]
> >      <END>
> > +    [
> > +        LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +        {
> > +             jjtThis.setPostfix(t == null ? u.image : t.image +
> u.image);
> > +             newlineAtEnd = true;
> > +        }
> > +    ]
> > +    {
> > +        return newlineAtEnd;
> > +    }
> >  }
> >
> > -void ElseStatement() : {}
> > +ASTBlock ElseStatement(ASTBlock previousBlock) :
> >  {
> > -   <ELSE_DIRECTIVE>
> > -    ( Statement() )* #Block
> > +    Token t = null, u = null;
> > +    ASTBlock block = null;
> > +    boolean afterNewline = false;
> > +}
> > +{
> > +   [ ( t = <WHITESPACE> )
> > +     {
> > +         previousBlock.setPostfix(t.image);
> > +         t = null;
> > +     }
> > +   ]
> > +   <ELSE>
> > +   (
> > +     [
> > +       LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +       {
> > +           jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
> > +           t = u = null;
> > +           afterNewline = true;
> > +       }
> > +     ]
> > +     ( LOOKAHEAD( { getToken(1).kind != END && (!afterNewline ||
> getToken(1).kind != WHITESPACE || getToken(2).kind != END) }) afterNewline
> = Statement(afterNewline) )*
> > +     {
> > +         block = jjtThis;
> > +         block.endsWithNewline = afterNewline;
> > +     }
> > +    )
> > +    #Block
> > +    {
> > +        return block;
> > +    }
> >  }
> >
> > -void ElseIfStatement() : {}
> > +ASTBlock ElseIfStatement(ASTBlock previousBlock) :
> >  {
> > -    <ELSEIF_DIRECTIVE> [<WHITESPACE>]
> > -    <LPAREN> Expression() <RPAREN>
> > -    ( Statement() )* #Block
> > +    Token t = null, u = null;
> > +    ASTBlock block = null;
> > +    boolean afterNewline = false;
> > +}
> > +{
> > +  [ ( t = <WHITESPACE> )
> > +    {
> > +        previousBlock.setPostfix(t.image);
> > +        t = null;
> > +    }
> > +  ]
> > +  <ELSEIF> ( <WHITESPACE> | <NEWLINE> )*
> > +  <LPAREN> Expression() <RPAREN>
> > +  (
> > +    [
> > +      LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +      {
> > +          jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
> > +          t = u = null;
> > +          afterNewline = true;
> > +      }
> > +    ]
> > +    ( LOOKAHEAD( { (getToken(1).kind != ELSEIF && getToken(1).kind !=
> ELSE && getToken(1).kind != END) && (!afterNewline || getToken(1).kind !=
> WHITESPACE || (getToken(2).kind != ELSEIF && getToken(2).kind != ELSE &&
> getToken(2).kind != END)) }) afterNewline = Statement(afterNewline) )*
> > +    {
> > +        block = jjtThis;
> > +        block.endsWithNewline = afterNewline;
> > +    }
> > +  )
> > +  #Block
> > +  {
> > +      return block;
> > +  }
> >  }
> >
> >  /**
> > @@ -1817,9 +1956,19 @@ void ElseIfStatement() : {}
> >   *   #set( expr )
> >   *   #set expr
> >   */
> > -void SetDirective() : {}
> > +boolean SetDirective() :
> > +{
> > +    Token t = null, u = null;
> > +    boolean endsWithNewline = false;
> > +}
> >  {
> > -    <SET_DIRECTIVE>([<WHITESPACE>] Reference() [<WHITESPACE>] <EQUALS>
> Expression() <RPAREN>
> > +    [ ( t = <WHITESPACE> )
> > +        {
> > +            jjtThis.setPrefix(t.image);
> > +            t = null;
> > +        }
> > +    ]
> > +    <SET_DIRECTIVE>(( <WHITESPACE> | <NEWLINE> )* Reference() (
> <WHITESPACE> | <NEWLINE> )* <EQUALS>  Expression() <RPAREN>
> >      {
> >          /*
> >           * ensure that inSet is false.  Leads to some amusing bugs...
> > @@ -1827,7 +1976,16 @@ void SetDirective() : {}
> >
> >          token_source.inSet = false;
> >      }
> > -    [<NEWLINE>] )
> > +    [
> > +        LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
> > +        {
> > +             jjtThis.setPostfix(t == null ? u.image : t.image +
> u.image);
> > +             endsWithNewline = true;
> > +        }
> > +    ] )
> > +    {
> > +        return endsWithNewline;
> > +    }
> >  }
> >
> >  /* ------------------------------------------------------------
> -----------
> > @@ -1902,18 +2060,21 @@ void MultiplicativeExpression() #void :
> >
> >  void UnaryExpression() #void : {}
> >  {
> > -     LOOKAHEAD(2)  [<WHITESPACE>]  <LOGICAL_NOT>  UnaryExpression()
> #NotNode(1)
> > -|   PrimaryExpression()
> > +    ( <WHITESPACE> | <NEWLINE> )*
> > +    (
> > +          <LOGICAL_NOT>  UnaryExpression() #NotNode(1)
> > +      |   PrimaryExpression()
> > +    )
> >  }
> >
> >  void PrimaryExpression() #void : {}
> >  {
> > -    [<WHITESPACE>]
> > +    ( <WHITESPACE> | <NEWLINE> )*
> >      (
> >       StringLiteral()
> >      | Reference()
> >      | IntegerLiteral()
> > -    | LOOKAHEAD(  <LBRACKET> [<WHITESPACE>]    ( Reference() |
> IntegerLiteral())     [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange()
> > +    | LOOKAHEAD(  <LBRACKET> ( <WHITESPACE> | <NEWLINE> )*    (
> Reference() | IntegerLiteral())     ( <WHITESPACE> | <NEWLINE> )*
> <DOUBLEDOT> ) IntegerRange()
> >      | FloatingPointLiteral()
> >      | Map()
> >      | ObjectArray()
> > @@ -1921,7 +2082,7 @@ void PrimaryExpression() #void : {}
> >      | False()
> >      | <LPAREN>  Expression()  <RPAREN>
> >       )
> > -    [<WHITESPACE>]
> > +    ( <WHITESPACE> | <NEWLINE> )*
> >  }
> >
> >
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/main/
> resources/org/apache/velocity/runtime/defaults/velocity.properties
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/main/resources/org/apache/velocity/
> runtime/defaults/velocity.properties?rev=1758416&r1=
> 1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/main/
> resources/org/apache/velocity/runtime/defaults/velocity.properties
> (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/main/
> resources/org/apache/velocity/runtime/defaults/velocity.properties Tue
> Aug 30 16:18:33 2016
> > @@ -223,4 +223,10 @@ introspector.restrict.classes = java.lan
> >  introspector.restrict.classes = java.lang.ThreadGroup
> >  introspector.restrict.classes = java.lang.ThreadLocal
> >
> > +# ------------------------------------------------------------
> ----------------
> > +# SPACE GOBBLING
> > +# ------------------------------------------------------------
> ----------------
> > +# Possible values: none, bc (aka Backward Compatible), lines, structured
> > +# ------------------------------------------------------------
> ----------------
> >
> > +space.gobbling = lines
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/InlineScopeVMTestCase.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/test/java/org/apache/velocity/test/
> InlineScopeVMTestCase.java?rev=1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/InlineScopeVMTestCase.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/InlineScopeVMTestCase.java Tue Aug 30
> 16:18:33 2016
> > @@ -60,7 +60,7 @@ public class InlineScopeVMTestCase exten
> >              Velocity.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, "true");
> >
> >          Velocity.setProperty(
> > -            Velocity.VM_PERM_INLINE_LOCAL, "true");
> > +                Velocity.VM_PERM_INLINE_LOCAL, "true");
> >
> >          Velocity.setProperty(
> >              Velocity.FILE_RESOURCE_LOADER_PATH,
> FILE_RESOURCE_LOADER_PATH);
> > @@ -68,6 +68,8 @@ public class InlineScopeVMTestCase exten
> >          Velocity.setProperty(
> >                  Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
> >
> > +        Velocity.setProperty("space.gobbling", "bc");
> > +
> >          Velocity.init();
> >      }
> >
> >
> > Modified: velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/ScopeTestCase.java
> > URL: http://svn.apache.org/viewvc/velocity/engine/trunk/
> velocity-engine-core/src/test/java/org/apache/velocity/test/
> ScopeTestCase.java?rev=1758416&r1=1758415&r2=1758416&view=diff
> > ============================================================
> ==================
> > --- velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/ScopeTestCase.java (original)
> > +++ velocity/engine/trunk/velocity-engine-core/src/test/
> java/org/apache/velocity/test/ScopeTestCase.java Tue Aug 30 16:18:33 2016
> > @@ -43,6 +43,7 @@ public class ScopeTestCase extends BaseT
> >          engine.setProperty("macro.provide.scope.control", "true");
> >          engine.setProperty("template.provide.scope.control", "true");
> >          engine.setProperty("vm.provide.scope.control", "true");
> > +        engine.setProperty("space.gobbling", "bc");
> >      }
> >
> >      public void testScopeGetLeakIntoInner()
> >
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@velocity.apache.org
> For additional commands, e-mail: dev-h...@velocity.apache.org
>
>

Reply via email to