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: 113
Last edited:

aeric

Expert
Licensed User
Longtime User
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
Thanks but I still don't see any example resembles your code in post #5.

The closest I can found is here:
 

Daestrum

Expert
Licensed User
Longtime User
jsr-223 scripting is standard across any script engine. if you can use one script engine, then any other is the same basically. As I use lots of different engines, incorporating velocity was easy. All I needed to know was velocity supported jsr-223, implementing was the easy part.
 

aeric

Expert
Licensed User
Longtime User
So far I have tested working fine in generating HTML in my Web API Template.
Instead of using [WebApp] (mini) Template Engine, I can now use $variable instead of $variable$.

B4X:
Dim strIndex As String = Utility.RenderTemplate("index.vm", Main.Config)
Main.Config.Put("VELOCITY_VIEW", strIndex)
Dim strMain As String = Utility.RenderTemplate("main.vm", Main.Config) ' main.vm is base template
Utility.ReturnHtml(strMain, Response)
 

Daestrum

Expert
Licensed User
Longtime User
Here's a little wrap for the script engine
example usage (the layout just has a webview added to display the html output)

B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim VTL As vtlScriptEngine
    Private WebView1 As WebView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
End Sub

Sub Button1_Click
    Dim myList As List
    myList.Initialize
    For a = 0 To 9
        myList.Add(a)
    Next

    Dim myScript As String
    myScript = $"
#set($myRes =
"<ul>
#foreach( $product in $allProducts )
    <li>Product: $product</li>
#end
</ul>
Printed: $now")"$

    DateTime.DateFormat="dd/MM/yyyy"

    VTL.Initialize
    Dim mm As Map ' yes I know I could have used CreateMap instead
    mm.Initialize
    mm.Put("allProducts",myList)
    mm.Put("now",DateTime.Date(DateTime.Now) & " " & DateTime.Time(DateTime.Now))
    VTL.putMap(mm)
   
    VTL.eval(myScript)
   
    WebView1.LoadHtml(VTL.get("myRes"))
End Sub
 

Attachments

  • jVTLEngine.zip
    2.9 KB · Views: 79
Last edited:

Daestrum

Expert
Licensed User
Longtime User
Couple of little things I found - that may help others

If using a text script as opposed to a file, the IDE picks up on the #if #else #end (as in it thinks it #if B4J etc) to get round it just write it as #{if} (hash-curly brace-if-curly brace)

B4X:
Won't work 
#if ($something == 24)
## do this
#else
## do that
#end

Will work
#{if} ($something == 24)
## do this
#{else}
## do that
#{end}

Also it uses java comparators ie '==' and not '=' '!=' and not '<>' etc
 

aeric

Expert
Licensed User
Longtime User
Here's a little wrap for the script engine
example usage (the layout just has a webview added to display the html output)

B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim VTL As vtlScriptEngine
    Private WebView1 As WebView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
End Sub

Sub Button1_Click
    Dim myList As List
    myList.Initialize
    For a = 0 To 9
        myList.Add(a)
    Next

    Dim myScript As String
    myScript = $"
#set($myRes =
"<ul>
#foreach( $product in $allProducts )
    <li>Product: $product</li>
#end
</ul>
Printed: $now")"$

    DateTime.DateFormat="dd/MM/yyyy"

    VTL.Initialize
    Dim mm As Map ' yes I know I could have used CreateMap instead
    mm.Initialize
    mm.Put("allProducts",myList)
    mm.Put("now",DateTime.Date(DateTime.Now) & " " & DateTime.Time(DateTime.Now))
    VTL.putMap(mm)
  
    VTL.eval(myScript)
  
    WebView1.LoadHtml(VTL.get("myRes"))
End Sub
I am getting error with B4XPages under Java 11. Are you compiling with different version?

B4X:
Compiling generated Java code.    Error
src\b4j\example\b4xmainpage.java:37: error: cannot access vtlscriptengine
public b4j.example.vtlscriptengine _vtl = null;
                  ^
  bad class file: C:\B4X\Additional\B4J\jVTLEngine.jar(/b4j/example/vtlscriptengine.class)
    class file has wrong version 63.0, should be 55.0
    Please remove or make sure it appears in the correct subdirectory of the classpath.
1 error

javac 11.0.1
 

Daestrum

Expert
Licensed User
Longtime User
Sorry, I compiled with java 19 (the only version on my laptop)

Here is the class I used to create it. Just add to a project and compile to library.
 

Attachments

  • vtlScriptEngine.bas
    1.2 KB · Views: 75

omo

Active Member
Licensed User
Longtime User
@aeric and @Daestrum, well done. First, can this velocity be used with B4A/B4i too in similar way you are using it in B4j? Second, aside using it to generate/wrap html, can it wrap CSS/JavaScript also or mix where possible either as inline or loaded from file assets?
 

Daestrum

Expert
Licensed User
Longtime User
The output is language independent, the template you write determines the output, be it html css php xml and probably B4X source with a bit of effort.

I don't know if it could be used on B4I or B4A tbh.
 

omo

Active Member
Licensed User
Longtime User
The output is language independent, the template you write determines the output, be it html css php xml and probably B4X source with a bit of effort.

I don't know if it could be used on B4I or B4A tbh.
That's good for being language independent. I could see the way you wrapped that html/webview and the rules look so simple enough to understand. It will be so interesting and useful if it can be used as B4A/I also.Thank you and welldone
 

Daestrum

Expert
Licensed User
Longtime User
Trivial sub generation for B4X
B4X:
    VTL.Initialize
    Dim script As String = $"#set ($myCode =
"Sub $name ($var As $type)
#evaluate($code)

End Sub")"$

    VTL.put("name", "testSub")
    VTL.put("var", "myVar")
    VTL.put("type", "Int")
    VTL.put("code",$"$var = $var + 1
Log($var)"$)
    VTL.eval(script)
    TextArea1.text = VTL.get("myCode")
 

omo

Active Member
Licensed User
Longtime User
Trivial sub generation for B4X...
Very good! If you generate for B4x, what of velocity setup in B4A/I, is it the same resources and procedure in #1 are what to follow to setup in B4A as example?
 

Daestrum

Expert
Licensed User
Longtime User
I do not use B4i so have no idea for that , B4A it 'should' work the same (assuming it has a script engine available at run-time) or use like aeric did in post #1 and see if it works.
 

aeric

Expert
Licensed User
Longtime User
I do not use B4i so have no idea for that , B4A it 'should' work the same (assuming it has a script engine available at run-time) or use like aeric did in post #1 and see if it works.
What if I tell you (and @omo ) that it works in B4A. 😝
I can't make it work with the script engine but using the template file, it works.

1686846215431.png
 

Daestrum

Expert
Licensed User
Longtime User
Well done. I suspected the script engine may not work.

I can't play with it anymore today lol Diablo IV is waiting for me :)
 

aeric

Expert
Licensed User
Longtime User
I suspected the script engine may not work.
The scripting engine is using javax which I read that it may not work in Android.

For B4i, it is using Objective-C so it also should not work.

Edit: Maybe someone want to try with the Mozilla Rhino
 

Daestrum

Expert
Licensed User
Longtime User
@aeric Using your method where do I put the template file? it can't find it wherever I put it lol.
 
Last edited:

omo

Active Member
Licensed User
Longtime User
Sorry, I compiled with java 19 (the only version on my laptop)

Here is the class I used to create it. Just add to a project and compile to library.
Ok, this is your script engine class to use, I will create time to try out my experiment.
What if I tell you (and @omo ) that it works in B4A. 😝
I can't make it work with the script engine but using the template file, it works.

View attachment 142959
@aeric, whaoo! Welldone, please find time to attach your simple sample test here to explore
 

Daestrum

Expert
Licensed User
Longtime User
Here is a wrap of your method (the one that works in B4A)

it makes the code shorter
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim vtl As VelocityLib
    Private WebView1 As WebView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    vtl.Initialize
End Sub

Sub Button1_Click
    vtl.putTemplate("test.vm")
    vtl.runTemplate
    WebView1.LoadHtml( vtl.retrieveContext("myTable"))
End Sub

The test.vm file
B4X:
#macro( tablerows $color $highlight $values $search)
  #foreach( $value in $values )
      #{if} ($value.contains($search.toUpperCase()) or $value.contains($search.toLowerCase()))
    <tr><td $width bgcolor=$color>$value</td></tr>
    #{else}
    <tr><td $width bgcolor=$highlight>$value</td></tr>
    #{end}
  #end
#end

#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )
#set( $color = "lightgreen" )
#set( $width = "width='200px'")

#set($myTable =
"<table>
  #tablerows( $color 'lightblue' $greatlakes 'R')
</table>" )

The output
1686852713561.png
 

Attachments

  • VelocityLib.bas
    1.4 KB · Views: 77
Last edited:
Top