How can I trim whitespace by Velocity
Asked Answered
C

8

22

I have a method called render_something which can creates a lot of whitespace, for example:

<a href="#">#render_something('xxx')</a>

The result can be:

<a href="#">     

           something that generate from redner_something


              </a> 

Which actually I want it to be like this:

<a href="#">something that generate from redner_something</a>

Does velocity has something like this?

#trim(#render_something('xxx'))
Communalize answered 21/12, 2011 at 17:43 Comment(0)
M
16

I just read this article on Velocity Whitespace Gobbling which suggests a few work-arounds including Velocity Whitespace Truncated By Line Comment.

This basically suggests commenting out line breaks by putting comments at the end of each line. It also suggests not indenting the code in your macros to prevent superfluous (one of my favourite words) spaces occurring.

TBH it's not a great solution but may suit your needs. Simply put ## at the end of each line in your macro and that will make things a little bit nicer... sort of

Moulin answered 23/12, 2011 at 11:49 Comment(1)
It can be an alternative. Thanks for suggestion. However, my situation cannot use your suggestion because I cannot change render_something.Communalize
P
9

It seems just java native trim() works.

$someValue.trim() works for me

Padre answered 27/2, 2013 at 12:10 Comment(1)
This does not work for me. I am sure my variable does not contains spaces (forbidden by regex) but it is added after the merge by velocity engine. Adding the trim() does not change anything. However adding the double hash ## works well.Hobson
D
3

Solution

In the class where you create the VelocityEngine, add a method as follows

public String trim(String str) {
    return str.trim()/*.replace("\n", "").replace("\r", "")*/;
}

then add the following to the VelocityContext that you create:

    context.put("trimmer", this);

and finally in the velocity template do the following

<a href="#">$trimmer.trim("#render_something('xxx')")</a>

Why does it work?

Although the behavior of Velocity is clearly define, it can be a bit tricky to see how it works sometimes. The separate trim()-method is necessary to get the char-sequence from the template into a Java method where you can call the actual trim() on the String. As far as I know there is no trim inside Velocity, but you always can call back to Java with tricks like this one.

The double-quotes are necessary because the #render_something is just a macro, not a function call, this means the results of the statements in the macro are put verbatim into the point where the macro is "executed".

Disseisin answered 23/12, 2011 at 0:34 Comment(0)
H
2

I struggled a while to find a straightforward solution to whitespace gobbling, so here the one I finally came up with. It is inspired from and Vadzim's answer and this page http://wiki.apache.org/velocity/StructuredGlobbingResourceLoader

The StructuredGlobbingResourceLoader we can find on the website has a complex behaviour and doesn’t get rid of any kind of whitespace, so I modified it to get the simple behaviour: "Delete any whitespace at the beginning of the lines, and add a comment at the end of each line" (which prevents the linebreak evaluation). The filter is applied on the input stream at loading time.

This kind of velocity template

#if($value)
    the value is $value
#end

is transformed to

#if($value)##
the value is $value##
#end##

Then if you want to have linebreaks or beginning of line whitespaces, you'll have to put($br,"\n") and put($sp," ") in your context like Vadzim's explained and explicitly use them in your template. This way of doing will allow you to keep indented templates, with maximum control.

take the class from this page http://wiki.apache.org/velocity/StructuredGlobbingResourceLoader change the extended class to the kind of loader your need (this one uses the webapp loader) replace the read() method with the code I provide use the class as your resource loader in your properties. Example for the webapp loader: webapp.resource.loader.class=...StructuredGlobbingResourceLoader

public int read() throws IOException {        
    int ch;
    switch(state){
        case bol: //beginning of line, read until non-indentation character
            while(true){
                ch = in.read();
                if (ch!=(int)' ' && ch!=(int)'\t'){
                    state = State.content;
                    return processChar(ch);
                }
            }

        case content:
            ch = in.read();
            return processChar(ch);

        //eol states replace all "\n" by "##\n"
        case eol1: 
            state = State.eol2;
            return (int)'#';

        case eol2:
            state = State.bol;
            return (int)'\n';

        case eof: 
            return -1;
    }
    return -1;
}

//Return the normal character if not end of file or \n
private int processChar(int ch){
    switch(ch){
    case -1:
        state = State.eof;
        return -1;
    case (int)'\n':
        state = State.eol1;
    return (int)'#';
    default:
        return ch;
    }
}

Any feedback on my implementation is welcome

Haarlem answered 14/5, 2015 at 2:47 Comment(0)
T
2

Inspired by Velocity Whitespace Truncated By Line Comment one could use block comments instead of line comments for a better looking result:

#foreach( $record in $records )#**
    *##if( $record.id == 0 )#**
    *##end
#end

With a decent syntax highlighting the comments aren't very obtrusive.

Toney answered 9/8, 2018 at 8:14 Comment(0)
S
1

Here is my alternative solution to velocity whitespace gobbling that allows tabbing template structure.

Each template text is preprocessed on first load in custom ResourceLoader:

private String enhanceTemplate(String body) {
    if (!body.startsWith("##preserveWhitespace")) {
        body = body.replaceAll("(##.*)?[ \\t\\r]*\\n+[ \\t\\r]*", Matcher.quoteReplacement("##\n"));
        body = body.trim();
    }
    return body;
}

This replaces all new lines and adjustent spaces with just one commented newline.

Line breaks and tailing spaces can be inserted explicitly with $br and $sp variables from default context:

private static final VelocityContext DEFAULT_CONTEXT = new VelocityContext(new HashMap<String, String>() {{
    put("sp", " ");
    put("br", "\n");
}});
Seraphina answered 18/9, 2012 at 16:33 Comment(0)
N
1

In some cases, I've had to essentially minimize my script like I would js or css. It works well, though it is not as easy for humans to read. Just one other option to eliminate the excess space:

<ul class="tabs">#foreach($par in $bodypars)#set( $parLen = ${_MathTool.toInteger($bodypars.size())} )#set( $parLn = $parLen - 1 )#set( $thClass = 'tb'+${parLn} )#set( $thaClass = '' )#if( $foreach.index == 1 )#set( $thClass = ${thClass}+' selected' )#set( $thaClass = ' selected' )#end#if($foreach.index != 0 && $parLen <= $maxTabs)#set ( $btitle = $_XPathTool.selectSingleNode($par,'item-subtitle') )<li class="${thClass}">#if($!btitle && $btitle != '')<a href="#" class="#cleanString($btitle.value.toLowerCase())${thaClass}">$_SerializerTool.serialize($btitle, true)</a>#end</li>#end#end</ul>
Njord answered 27/5, 2015 at 13:48 Comment(0)
D
0

You can use standard java trim, taking attention to your variable if are a object instead string.

$string.trim()  //work fine
$object.trim()  //exception

Have a good day!

Dock answered 2/9, 2020 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.