B4J Code Snippet [web]Velocity Template Language (VTL)

This example is based on https://velocity.apache.org/engine/2.3/developer-guide.html#how-velocity-works

Download additional libraries:
velocity-engine-core-2.3.jar
commons-lang3-3.12.0.jar
slf4j-api-2.0.7.jar
slf4j-simple-2.0.7.jar

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
#End Region
#AdditionalJar: commons-lang3-3.12.0
'#AdditionalJar: commons-collections4-4.4
#AdditionalJar: velocity-engine-core-2.3
#AdditionalJar: slf4j-api-2.0.7
#AdditionalJar: slf4j-simple-2.0.7
Sub Process_Globals

End Sub

Sub AppStart (Args() As String)
    Dim Velocity As JavaObject
    Velocity.InitializeStatic("org.apache.velocity.app.Velocity")
    Velocity.RunMethod("init", Null)
 
    Dim Context As JavaObject
    Context.InitializeNewInstance("org.apache.velocity.VelocityContext", Null)
    Context.RunMethod("put", Array("name", "Velocity"))
 
    Dim Template As JavaObject
    Template = Template.InitializeNewInstance("org.apache.velocity.Template", Null)
    Template = Velocity.RunMethod("getTemplate", Array As String("test.vm"))
 
    Dim StringWriter As JavaObject
    StringWriter.InitializeNewInstance("java.io.StringWriter", Null)
    Template.RunMethodJO("merge", Array( Context, StringWriter ) )
 
    Dim Content As String = StringWriter.RunMethod("toString", Null)
    StringWriter.RunMethod("close", Null)
 
    Log(Content)
End Sub
 

Attachments

  • Velocity.zip
    1.3 KB · Views: 117
Last edited:

aeric

Expert
Licensed User
Longtime User
I just found this project yesterday and I am really new to it. By luck, I am able to make it works.
This template engine may not as advanced as Freemarker but it seems simple to use and small.
 

Daestrum

Expert
Licensed User
Longtime User
It can be used as a script engine too, so may be easy to wrap for B4X.
 

Daestrum

Expert
Licensed User
Longtime User
Small snippet - (not a wrap of the scriptengine) but it can be wrapped like I did for javascript-jruby-jgroovy etc
B4X:
'Non-UI application (console / server application)
#Region Project Attributes 
    #CommandLineArgs:
    #MergeLibraries: True 
    #AdditionalJar: commons-lang3-3.12.0
    #AdditionalJar: velocity-engine-core-2.3
    #AdditionalJar: velocity-engine-scripting-2.3
    #AdditionalJar: slf4j-api-2.0.7
    #AdditionalJar: slf4j-simple-2.0.7
#End Region

Sub Process_Globals
    Dim engineManager As JavaObject
    Dim vtlEngine As JavaObject
    Dim vtl As JavaObject
End Sub

Sub AppStart (Args() As String)
    engineManager.InitializeNewInstance("javax.script.ScriptEngineManager",Null)
    vtl.InitializeNewInstance("org.apache.velocity.script.VelocityScriptEngineFactory",Null)
    engineManager.RunMethod("registerEngineName",Array("velocity",vtl))

    vtlEngine = engineManager.RunMethod("getEngineByName",Array("velocity"))
    
    vtlEngine.RunMethod("put",Array("world","people"))
    Log(vtlEngine.RunMethod("eval",Array As String("Hello $world")))
End Sub
 

aeric

Expert
Licensed User
Longtime User
Small snippet - (not a wrap of the scriptengine) but it can be wrapped like I did for javascript-jruby-jgroovy etc
B4X:
'Non-UI application (console / server application)
#Region Project Attributes 
    #CommandLineArgs:
    #MergeLibraries: True 
    #AdditionalJar: commons-lang3-3.12.0
    #AdditionalJar: velocity-engine-core-2.3
    #AdditionalJar: velocity-engine-scripting-2.3
    #AdditionalJar: slf4j-api-2.0.7
    #AdditionalJar: slf4j-simple-2.0.7
#End Region

Sub Process_Globals
    Dim engineManager As JavaObject
    Dim vtlEngine As JavaObject
    Dim vtl As JavaObject
End Sub

Sub AppStart (Args() As String)
    engineManager.InitializeNewInstance("javax.script.ScriptEngineManager",Null)
    vtl.InitializeNewInstance("org.apache.velocity.script.VelocityScriptEngineFactory",Null)
    engineManager.RunMethod("registerEngineName",Array("velocity",vtl))

    vtlEngine = engineManager.RunMethod("getEngineByName",Array("velocity"))
    
    vtlEngine.RunMethod("put",Array("world","people"))
    Log(vtlEngine.RunMethod("eval",Array As String("Hello $world")))
End Sub
Thanks for the example. The difference I can see here is it doesn't use a template file.
I also learn that Velocity also have a Tool but I yet to understand how it works. There are a lot of potential to explore if you find time.
 

Daestrum

Expert
Licensed User
Longtime User
Minor change - makes the log better (without the data after the result)
B4X:
    vtlEngine.RunMethod("put",Array("world","people"))
    
    vtlEngine.RunMethod("eval",Array As String($"#set($res = "Hello $world")"$))
    Dim s As String = vtlEngine.RunMethod("get",Array("res"))
    Log(s)
 

Daestrum

Expert
Licensed User
Longtime User
Bit of an expanded example
B4X:
    Dim myList As List
    myList.Initialize
    For a = 0 To 10 
        myList.Add(a)
    Next

    Dim myScript As String ' the script
    myScript = $"
#set($myRes = 
"<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>")"$
    vtlEngine.RunMethod("put",Array("allProducts",myList))  ' pass the java array to the engine
    vtlEngine.RunMethod("eval",Array(myScript)) ' run the script
    Log(vtlEngine.RunMethod("get",Array("myRes"))) ' get the result
 

aeric

Expert
Licensed User
Longtime User
Bit of an expanded example
B4X:
    Dim myList As List
    myList.Initialize
    For a = 0 To 10
        myList.Add(a)
    Next

    Dim myScript As String ' the script
    myScript = $"
#set($myRes =
"<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>")"$
    vtlEngine.RunMethod("put",Array("allProducts",myList))  ' pass the java array to the engine
    vtlEngine.RunMethod("eval",Array(myScript)) ' run the script
    Log(vtlEngine.RunMethod("get",Array("myRes"))) ' get the result
I am curious you are just started playing or you already use this scripting engine for a long time. 😝
 

Daestrum

Expert
Licensed User
Longtime User
I only started with VTL when I saw your post today.
 

Daestrum

Expert
Licensed User
Longtime User
I tried their html table generation.
For some reason it dislikes B4X Classes and Types, as I found no way to call a sub in a class from the script. (I think it's the underscore causing it to ignore the method names)
If I created a java class I could then call the methods no problem. (Maybe someone else could have a look - fresh eyes and all that)

example
B4X:
...
    Dim script2 As String
    script2=$"
#set($myPerson =
"<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
#foreach( $p in $peeps )
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
#end
</table>")"$   
    Dim peps As List
    peps.Initialize
   
    For t = 0 To 4
        Dim pp As JavaObject   
        pp.InitializeNewInstance("b4j.example.main.Members",Null)
        pp.RunMethod("setName",Array("Fred Bloggs" & t))
        pp.RunMethod("setAge",Array(12 + t))
        pp.RunMethod("setAddress",Array("123 the road" & t))
        pp.RunMethod("setPostcode",Array("RM10 " & t & "aa"))
        peps.Add(pp)
    Next
   
    vtlEngine.RunMethod("put",Array("peeps",peps))
    vtlEngine.RunMethod("eval",Array(script2))
    Dim mytable As String = vtlEngine.RunMethod("get",Array("myPerson"))
    Log(mytable)

    ' next line for checking in Edge to see the table
    'File.WriteString("c:/temp","vtlText.html",mytable)
End Sub


#if java
public static class Members{
    private String  name;
    private Integer age;
    private String  address;
    private String  postcode;
   
    public Members(){}
   
    public void setName(String n){
        this.name = n;
    }
    public void setAge(Integer a){
        this.age = a;
    }
    public void setAddress(String addy){
        this.address = addy;
    }
    public void setPostcode(String p){
        this.postcode = p;
    }
   
    public String getName(){
        return this.name;
    }
    public Integer getAge(){
        return this.age;
    }
    public String getAddress(){
        return this.address;
    }
    public String getPostcode(){
        return this.postcode;
    }
}
#End If
 

aeric

Expert
Licensed User
Longtime User
I tried their html table generation.
For some reason it dislikes B4X Classes and Types, as I found no way to call a sub in a class from the script. (I think it's the underscore causing it to ignore the method names)
If I created a java class I could then call the methods no problem. (Maybe someone else could have a look - fresh eyes and all that)

example
B4X:
...
    Dim script2 As String
    script2=$"
#set($myPerson =
"<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
#foreach( $p in $peeps )
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
#end
</table>")"$  
    Dim peps As List
    peps.Initialize
  
    For t = 0 To 4
        Dim pp As JavaObject  
        pp.InitializeNewInstance("b4j.example.main.Members",Null)
        pp.RunMethod("setName",Array("Fred Bloggs" & t))
        pp.RunMethod("setAge",Array(12 + t))
        pp.RunMethod("setAddress",Array("123 the road" & t))
        pp.RunMethod("setPostcode",Array("RM10 " & t & "aa"))
        peps.Add(pp)
    Next
  
    vtlEngine.RunMethod("put",Array("peeps",peps))
    vtlEngine.RunMethod("eval",Array(script2))
    Dim mytable As String = vtlEngine.RunMethod("get",Array("myPerson"))
    Log(mytable)

    ' next line for checking in Edge to see the table
    'File.WriteString("c:/temp","vtlText.html",mytable)
End Sub


#if java
public static class Members{
    private String  name;
    private Integer age;
    private String  address;
    private String  postcode;
  
    public Members(){}
  
    public void setName(String n){
        this.name = n;
    }
    public void setAge(Integer a){
        this.age = a;
    }
    public void setAddress(String addy){
        this.address = addy;
    }
    public void setPostcode(String p){
        this.postcode = p;
    }
  
    public String getName(){
        return this.name;
    }
    public Integer getAge(){
        return this.age;
    }
    public String getAddress(){
        return this.address;
    }
    public String getPostcode(){
        return this.postcode;
    }
}
#End If
wow, I think you already fall in love with this library. You have written so much tests. 😝
 

Daestrum

Expert
Licensed User
Longtime User
No I haven't to be honest. (I rarely use any B4X code as I don't need cross platform code)
 

aeric

Expert
Licensed User
Longtime User
I tested with type, it doesn't work.

B4X:
Type Members (Name As String, Age As Int, Address As String, Postcode As String)

B4X:
For t = 0 To 4
    Dim pp2 As Members
    pp2.Initialize
    pp2.Name = "Aeric " & t
    pp2.Age = 12 + t
    pp2.Address = "123 the road " & t
    pp2.Postcode = "RM10 " & t & "aa"
    peps.Add(pp2)
Next

Logs:
Waiting for debugger to connect...
Program started.
<table>
    <tr>
        <th width='200px'>Name</th>
        <th>Age</th>
        <th>Address</th>
        <th>Postcode</th>
    </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
        <tr>
            <td>$p.getName()</td>
            <td>$p.getAge()</td>
            <td>$p.getAddress()</td>
            <td>$p.getPostcode()</td>
        </tr>
</table>
Program terminated (StartMessageLoop was not called).

Edit: Using a standard class also not working either.
 
Last edited:

aeric

Expert
Licensed User
Longtime User
Have you tried using B4X Maps?
Maps don't work too.

Edit:
It works for Map!

B4X:
For t = 0 To 4
    Dim pp3 As Map
    pp3.Initialize
    pp3.Put("Name", "Aeric " & t)
    pp3.Put("Age", 12 + t)
    pp3.Put("Address", "123 the road " & t)
    pp3.Put("Postcode", "RM10 " & t & "aa")
    peps.Add(pp3)
Next

HTML:
#foreach( $p in $peeps )
        <tr>
            <td>$p.get('Name')</td>
            <td>$p.get('Age')</td>
            <td>$p.get('Address')</td>
            <td>$p.get('Postcode')</td>
        </tr>
#end

edit: strange that it doesn't like double quotes but works with single quote.
 
Last edited:

aeric

Expert
Licensed User
Longtime User
Another example using evaluate method without loading template file into the engine.

B4X:
Public Sub Test5
    Dim Velocity As JavaObject
    Velocity.InitializeStatic("org.apache.velocity.app.Velocity")
    
    'Dim VelocityEngine As JavaObject
    'VelocityEngine.InitializeNewInstance("org.apache.velocity.app.VelocityEngine", Null)
    
    Velocity.RunMethod("init", Null)
    
    Dim Context As JavaObject
    Context.InitializeNewInstance("org.apache.velocity.VelocityContext", Null)
    Context.RunMethod("put", Array("name", "Velocity"))
    
    Dim Content As String = File.ReadString(File.DirAssets, "index.vm")
    Content = $"#set($content = "${Content}")
    #evaluate($content)"$

    Dim StringWriter As JavaObject
    StringWriter.InitializeNewInstance("java.io.StringWriter", Null)
    Velocity.RunMethod("evaluate", Array(Context, StringWriter, "", Content))
    Content = StringWriter.RunMethod("toString", Null)
    StringWriter.RunMethod("close", Null)
    Log(Content)
End Sub

There are 2 ways to use the Velocity Engine.

Singleton Model:
B4X:
Dim Velocity As JavaObject
Velocity.InitializeStatic("org.apache.velocity.app.Velocity")

Separate Instance:
B4X:
Dim VelocityEngine As JavaObject
VelocityEngine.InitializeNewInstance("org.apache.velocity.app.VelocityEngine", Null)
 
Last edited:

Daestrum

Expert
Licensed User
Longtime User
How did you come out with the Scripting Engine?
I found it by a search as the examples on apache velocity pages were not too advanced.

Have a look here - it has really good examples (at bottom of page for more) Good Info
 
Last edited:
Top