Thanks!

I'm sure it will happen one day, Nathan.

Yes, we ought to find some new philosophical debate...

  Claude

On 30/08/2016 18:48, Nathan Bubna wrote:
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






---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@velocity.apache.org
For additional commands, e-mail: dev-h...@velocity.apache.org

Reply via email to