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