I have written pretty basic Velocity template which uses #foreach directive. When I run it, I get the following message in the log:
[warn] The directive.foreach.iterator.name property has been
deprecated. It will be removed (along with $velocityHasNext itself )
in Velocity 2.0. Instead, please use $foreach.hasNext to access this
value from now on.
But the problem is that I don't use this property, at least explicitly. I just use #foreach loop in its basic form, exactly as shown in the user guide.
Below is the simple code which yields this log warning. Velocity version is 1.7.
final Properties properties = new Properties();
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.init(properties);
StringWriter stringWriter = new StringWriter();
Map<String, Object> velocityVars = new HashMap<>();
List<Date> dates = new ArrayList<>();
dates.add(new Date());
dates.add(new Date());
velocityVars.put("dates", dates);
VelocityContext velocityContext = new VelocityContext(velocityVars);
String template =
"foo\n" +
"#foreach($d in $dates)\n" +
" $d\n" +
"#end \n" +
"bar";
velocityEngine.evaluate(velocityContext, stringWriter, "blah", template);
System.out.println(stringWriter.toString());
So the question is - why is this warning logged and how do I structure my code to prevent it from appearing?
directive.foreach.iterator.name is not something found in code, but in a velocity.properties file. And for backwards compatibility reasons, this deprecated definition is still present in the default configuration.
The warning is displayed during the initialization of the foreach directive itself, so it's not triggered by something you're doing. You can safely ignore this warning, but if you definitely don't want to see it anymore, you could:
Raise the logging level above WARN
Write you own custom properties file, or just add from Java in the Properties object that you're creating, empty values for directive.foreach.counter.name, directive.foreach.iterator.name and directive.foreach.counter.initial.value.
Or, extract org/apache/velocity/runtime/defaults/velocity.properties from the velocity jar in your classpath directory, and remove from it the deprecated settings.
Related
I am fairly new to Freemarker and to template engine in general.
Basicly I want to be able to generate two XML files (from two different templates) using java methods to feed data to the template.
For example:
myTemplate1.ftl contain an opening and closing "res" tag with getRes() method as value (return 1 for exemple)
myTemplate2.ftl contain an opening and closing "result" tag and takes getResult() method as value (return 2 for exemple)
How can I write one class Main to process this without being specific (I do not want to write line per line, because then it would be pointless to create an engine)
Can you please help me understand how it work through an example if possible ?
Regards
EDIT with all the new informations :
#fustaki This is very frustrating. It seems I need to go step by step to understand what I'm doing. So... Here is a very dumb example of where I am :
template1.ftl
<Email>${item.getEmail()}</Email><Language>${item.getLanguage()}</Language>
FillMyTemplate1.java
public String getEmail(){ return "test#test.com" }
public String getLanguage(){ return "EN" }
I am using a property file in order to use introspection : prop.properties which contain :
template1=fr.freemarker.test.xml.FillMyTemplate1
MainTest.java
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
InputStream input = new FileInputStream("prop.properties");
prop.load(input);
Class<?> classe = Class.forName(prop.getProperty(args[0])); //where args[0] is "template1"
Configuration config=new Configuration();
config.setClassForTemplateLoading(MainTest.class, "templates");
config.setObjectWrapper(new DefaultObjectWrapper());
Template template=config.getTemplate(args[0]+".ftl");
Map<String, Object> dataModel = new HashMap<String, Object>();
Writer consoleWriter = new OutputStreamWriter(System.out);
dataModel.put("item", classe.newInstance());
template.process(dataModel, consoleWriter);
}
And the result : <Email>test#test.com</Email><Language>EN</Language>
In order to understand what you said, I need to know how a "data-provider" would look like in my case ? How can I get ride of this "item" in my map ? I understand the principle but not the technical solution...
Your Main process is in charge of producing the data (List, Map or other structured object) and templates will use the data to render your XMLs.
The tags used in the templates are independent from the data inside them.
Example where data are stored in a List that can be retrieved with a getItems() or in general is accessible from the freemarker engine.
<?xml>
<res>
<#list items as item >
<item>${item}</item>
</#list>
</res>
You can create as much templates (views) as you want using the same List items.
I hope this clarify your troubles.
here is good example of freemarker. this application generate spring mvc crud operation
using a freemarker template.
springmvcfreemarker
Came across this issue, while implementing i18n using spring resource bundle, for a spring based java project, which has a html+js UI.
I need to read all content from a properties file, for a particular locale, and pass this info to the client side, so that the relevant messages can be shown on the UI.
However, the ResourceBundleMessagesource/ReloadableResourceBundleMessagesource objects seem to allow retrieving only a single message at a time.
ReloadableResourceBundleMessageSource msgSource = new ReloadableResourceBundleMessageSource();
//Methods to get only a single message at a time
String message = msgSource.getMessage("edit.delete.success", null, new Locale(localeString));
I am currently using java.util.ResourceBundle, and looping over the object using it's keyset.
rB = ResourceBundle.getBundle("messages", new Locale(localeString));
Map<String, String> msgs = new HashMap<>();
for(String messageKey : rB.keySet()){
msgs.put(messageKey, rB.getString(messageKey));
}
Q1 : Is there any better/more elegant way to solve this?
Q2 : What is the reason for the authors to not allow for accessing all properties from a file ?
what am I missing ? I can't get why my transformation is not schema-aware.
Ref:
http://www.saxonica.com/documentation/schema-processing/satransformapi.html
http://www.saxonica.com/documentation9.4-demo/html/changes/intro92/install92.html
I know the documents are fine, xsd/xslt/xml files are processed by other systems and it works fine. I was trying to create a desktop command line tool for myself.
source code
def main(args: Array[String])
{
System.setProperty( "javax.xml.transform.TransformerFactory", "com.saxonica.config.EnterpriseTransformerFactory")
val factory = new EnterpriseTransformerFactory()
factory.setAttribute(FeatureKeys.SCHEMA_VALIDATION, new Integer(Validation.STRICT))
val schemaXXX = new StreamSource( new File("PATH/to/xxx.xsd") )
val schemaYYY = new StreamSource( new File("PATH/to/yyy.xsd") )
factory.addSchema(schemaXXX)
factory.addSchema(schemaYYY)
val XSLT = new StreamSource(new File("PATH/to/zzz.xslt"))
val transformer = factory.newTransformer(XSLT)
val input = new StreamSource(new File("PATH/to/file.xml"))
val result = new StringWriter();
transformer.transform(input, new StreamResult(result))
println(result.toString())
}
Result:
The transformation is not schema-aware, so the source document must be untyped
A stylesheet in Saxon-EE is considered schema-aware if it explicitly uses xsl:import-schema, or if the XSLT compiler used to compile it is explicitly set to be schema aware. This is easier done using the s9api interface (XsltCompiler.setSchemaAware(true)), but it can also be done using JAXP by setting the property FeatureKeys.XSLT_SCHEMA_AWARE ("http://saxon.sf.net/feature/xsltSchemaAware") on the TransformerFactory. The reason you have to set this explicitly is that processing untyped documents is faster if the stylesheet knows at compile time that everything will be untyped, so we don't want people to incur extra costs when they move to Saxon-EE if they aren't using this feature.
In future please feel free to raise support questions at saxonica.plan.io where we aim to give a response within 24 hours.
The following is an example of what I want to do.
I have a bunch of files such as test1.vm:
Welcome ${name}. This is test1.
Then I have a file called defaults.vm:
#set($name = "nmore")
I want render test1.vm (and the other test files) with the variable(s) in defaults.vm without using #parse as I would have to modify all the test files.
Is there a way to do this from within the accompanying java file?
I'm not sure if you have any constraints or any other specific requirements, but if you don't have you tried to use Velocity API? Something like this:
Context context = new VelocityContext();
Template template = Velocity.getTemplate("src/main/resources/defaults.vm");
template.merge(context, NullWriter.NULL_WRITER);
StringWriter writer = new StringWriter();
Template toBeParsedTemplate = Velocity.getTemplate("src/main/resources/test1.vm");
toBeParsedTemplate.merge(context, writer);
String renderedContent = writer.getBuffer().toString();
System.out.println(renderedContent);
The idea is that you fill in the Context object with the variables generated from defaults.vm and use the same context to evaluate test1.vm.
I've tried this using Velocity 1.7 and commons-io 2.4 (for the NullWriter) seems to be working fine, but I'm not sure if this can fit into your requirement or you're looking into other alternatvies (not using Velocity API).
More info on the Context object here:
http://velocity.apache.org/engine/devel/developer-guide.html#The_Context
Hope that helps.
Does anyone know if it is possible to get templates from different paths with velocity? After initialization Velocity refuses to change the "file.resource.loader.path".
This is my code:
public Generator(){
Properties p = new Properties();
p.setProperty("resource.loader", "file");
p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
p.setProperty("file.resource.loader.path", "");
Velocity.init(p);
}
The templates can be located in different locations ( the user can select one with a file dialog ). So I have this code upon fetching the template out of velocity
private Template fetch (String templatePath) {
out_println("Initializing Velocity core...");
int end = templatePath.lastIndexOf(File.separator);
Properties p = new Properties();
p.setProperty("file.resource.loader.path", templatePath.substring(0, end));
Velocity.init(p);
return Velocity.getTemplate(templatePath.substring(end+1));
}
This is not working. It seems that once Velocity is initialized it can't be reset with different properties. Any suggestions on how to solve this problem?
Possible Program flow:
User selects group that needs to be filled into the template
User selects a template to use (can be located anywhere on the hdd)
User presses generate
Velocity can be used in two ways: the singleton model or the separate instance model. You are currently using the singleton model in which only one instance of the Velocity engine in the JVM is allowed.
Instead, you should use the separate instance model which allows you to create multiple instances of Velocity in the same JVM in order to support different template directories.
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "path/to/templates");
ve.init();
Template t = ve.getTemplate("foo.vm");
Adding to the points above:
Even if one is using non-singleton model i.e using VelocityEngine object. Multiple paths can be configured by giving comma separated values to the property.
[file.resource.loader.class=path1,path2]
In such a case velocity engine will look for template in path1 first and then in path2
Consider instead of using singleton Velocity class creating and initializing new VelocityEngine before step 3.
In my case I am using Velocity with Servlets in an Eclipse Dynamic Web Project.
I couldn't actually reset the path, but I could put a subdirectory under /WebContent folder and then organize my templates that way... and have nested subdirectories as well.
RequestDispatcher requestDispatcher =
request.getRequestDispatcher("/velocity_templates/index.vm");
This simple solution was all I needed ... didn't need to mess with velocity.properties in web.xml or setting them programmatically (in each case, neither approach worked for me unfortunately when I tried).
Note that when I do template includes with #parse(..) command, I need to use the same path prefix inside the template .vm file as I did in the example code for my servlet.