heres my test in java
public class person
{
public String name;
public int age;
public String getName() { return name; }
public int getAge() { return age; }
}
In my function I create a number of person objects
and add it into an list
ArrayList<person> arr = new ArrayList<person>()
arr.add(person1);
arr.add(person2); etc etc
in the string template group file I have got
test(arr) ::= <<
<table>
$arr: {a|
<tr><td>$a.name$</td><td>$a.age$</td></tr>
}$
</table>
>>
this is called from my list template
list (arr) ::= <<
$test(arr)$
... and other page details etc
>>
in version4 for I get template not found message with some stack trace as follows
Caused by: java.lang.ClassCastException: java.util.ArrayList
at org.antlr.runtime.tree.RewriteRuleTokenStream.nextNode(RewriteRuleTokenStream.java:58)
at org.stringtemplate.v4.compiler.STParser.subtemplate(STParser.java:1563)
at org.stringtemplate.v4.compiler.STParser.mapTemplateRef(STParser.java:3692)
but I can list an array as follows and it prints the object representation in string format
(also I can use a map - ie key value pairs works ok too)
in string template
test(arr) :: =<<
<p>
$arr; seperator="</br>"$
</p>
>>
how do we iterate list of object to print field values using string template grop in V4
am I using it in a wrong format/syntax?
any help/points would be greateful
note: we set $ as delimiter using new STGroupString("", templateGroup, '$', '$');
You are missing the terminating $ on your expression: $a.age should be $a.age$
Related
I have a handler in my rest easy lamda application
with the url: http://localhost:8080/store/search/v1/suggest
public List<Map<String, Object>> storeSearch(
#RestHeader("id") String id,
#RestQuery final String q,
#RestQuery final String columns) {
if (StringUtils.isBlank(q)) {
logger.error("Search query is empty");
return Collections.EMPTY_LIST;
}
The app is doing well for below cases but failing if q is empty
http://localhost:8080/store/search/v1/suggest?q=ab
http://localhost:8080/store/search/v1/suggest
failing here
http://localhost:8080/store/search/v1/suggest?q=
Can you please suggest me what I am missing here.
My database contain a column which is the type of varchar.
In that column i am storing value in the form [1,2] .
I need to get first value '1'.
I need to write jpql query using #query of spring data jpa.
I have decided to remove the braces of the value by using substring function then i need to convert it to array.
So, I have tried like this for the substring extraction.
SUBSTRING(u.output,1,LENGTH(u.output-2))
Is this syntax correct for substring extraction? how convert it to array.
If you cannot normalise the database then I would normalise the domain model via a JPA Converter.
You can these just load the entity and navigate the array as normal.
https://www.baeldung.com/jpa-attribute-converters
#Entity
public MyEntity{
#Convert(converter = MyArrayConverter.class)
int [] values;
}
Create a JPA Converter:
#Converter
public class MyArrayConverter implements
AttributeConverter<Integer [], String> {
#Override
public String convertToDatabaseColumn(int [] values) {
//convert int [] to string "[1,2,3]"
}
#Override
public Integer [] convertToEntityAttribute(String dbValue) {
//convert string "[1,2,3]" to int []
}
}
I am having trouble with having a formatter within a forloop in Apache Velocity.
#set( $array = ["10000", "3000", "13.456", "1111.13"] )
<ul>
#foreach( $a in $array)
<li>$formatter.print($a)</li>
#end
</ul>
This would be evaluated and print the original expression as a string 4 times
$formatter.print($a)
$formatter.print($a)
$formatter.print($a)
$formatter.print($a)
instead of
10,000.00
3,000.00
13.456
1,111.13
However it seems to work fine with the formatter outside of the scope from the forloop
<p>$formatter.print(123456)</p>
This would work as usual
Can anyone helps me figuring out how to reference a property (in this case $formatter) within a for loop ?
This can happen when one of the following condition is true:
1) The model passed to the velocity does not have the variable "formatter"
2) The method print is returning null or it does not exist
3) The method print accepts a parameter of the wrong type. Try to pass Object...
The following code works for me (notice that I am using an array of double and not any more an array of string):
package test;
import java.io.StringWriter;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
public class VelocityHelloWorld
{
public static void main( String[] args )
throws Exception
{
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
ve.init();
VelocityContext context = new VelocityContext();
context.put("formatter", new Formatter());
Template t = ve.getTemplate( "helloworld.vm" );
StringWriter writer = new StringWriter();
t.merge( context, writer );
System.out.println(writer.toString());
}
}
Velocity:
#set( $array = [10.00 , 20.00, 13.456, 1111.13] )
<ul>
#foreach( $a in $array)
<li>$formatter.print($a)</li>
#end
</ul>
Formatter:
package test;
public class Formatter {
public String print (Object d) {
String s = d.getClass().getName() + ": " + d.toString();
return s;
}
}
The template shows the same behaviour like yours if I substitute in
print (Double d)
Double with Float.
Long story short... I think that you probably need to check the parameter passed to your method.
Of course I think you should use Double and create the array as a list of double and not as a list of strings.
Try ${myref.doIt($var)} syntax to reference context variable. This ensures Velocity does not parse context names incorrectly within strings.
I'm using StringTemplate 4 to generate some Java source files.
The templates are really simple, e.g.:
QueryHandler(method) ::="
public class Obtenir<method.name>Handler extends QueryHandler\<List\<<method.name>Db>> implements IQueryHandler\<List\<<method.name>>>{
private IQuery\<List\<<method.name>Db>> query;
private <method.name>Converter converter;
#Inject
public Obtenir<method.name>Handler(IQuery\<List\<<method.name>Db>> query, <method.name>Converter converter, IStatisticsCollecter theStatsCollecter){
super(theStatsCollecter);
if(query == null){
throw new IllegalArgumentException(\"The query argument cannot be null\");
}
if(converter == null){
throw new IllegalArgumentException(\"Illegal argument for converter(null)\");
}
this.query = query;
this.converter = converter;
}
public List\<<method.name>> handle(Object... params) throws JdbcException {
final String method = \"obtenir<method.name>\";
DaoQueryStatusCallable status = new DaoQueryStatusCallable();
List\<<method.name>Db> result = invoke(query, status, method);
return converter.convert(result);
}
}
"
The code is even simpler:
STGroup group = new STGroupFile("src/main/resources/QueryHandler.stg");
ST wsTemplate = group.getInstanceOf("QueryHandler");
wsTemplate.add("method", m);
System.out.println(wsTemplate.render());
The template lines are separated by Unix EOLs (\n).
When I execute the code, StringTemplate is emitting a warning "QueryHandler.stg 1:25: \n in string".
The result is correct, but I'd still like to get rid of this message.
Anybody ever had this problem and knows how to solve it?
t() ::= "..." is meant only for single lines. Please use
t() ::= <<
...
>>
to get multi-line templates.
Ter
I would like to parse a file where the first line may or may not contain a definition of a "project" name (like with Pascal's program keyword), and if not, use the name of the file that is being parsed as default. Simplified:
#members{ String projectName; }
project : {projectName = ...} // name of parsed file as default
( PROJECTNAMEKEYWORD ID {projectName = $ID.text;} )?
otherstuff {/*...*/};
Is this even possible? I found this surprisingly hard to find out using google or the antlr manual. By its documentation, input.getSourceName() should be what I am looking for, but it always returns null, and debugging lead me to the class ANTLRStringStream that has a name field whose value is returned by this method but never set.
Simply create a custom constructor in your parser that takes a string that represents your file.
A demo:
grammar T;
#parser::members {
private String projectName;
public TParser(String fileName) throws java.io.IOException {
super(new CommonTokenStream(new TLexer(new ANTLRFileStream(fileName))));
projectName = fileName;
}
public String getProjectName() {
return projectName;
}
}
parse
: (PROJECT ID {projectName = $ID.text;})? EOF
;
PROJECT : 'project';
ID : ('a'..'z' | 'A'..'Z')+;
SPACE : (' ' | '\t' | '\r' | '\n') {skip();};
which can be tested with:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
// 'empty.txt' is, as the name suggests, an empty file
TParser parser = new TParser("empty.txt");
parser.parse();
System.out.println(parser.getProjectName());
// 'test.txt' contains a single line with the words: 'project JustATest'
parser = new TParser("test.txt");
parser.parse();
System.out.println(parser.getProjectName());
}
}
If you run the Main class, the following will be printed to your console:
empty.txt
JustATest