Keep indentation when including template with FreeMarker - freemarker

When I include a template with <#include ...> directive, the content of that template is inserted starting from column 0 and not where the include tag was located. Is it possible to tell FreeMarker to respect indentation?

You could use macros and pass them the indentation as a parameter. Consider, for example, building a list of courses in YAML format, where each one has a list of students.
report.ftl:
<#include "course.ftl" parse=true>
<#include "faculty.ftl" parse=true>
<#include "student.ftl" parse=true>
<#list report as course>
<#newCourse course = course indent = 0/>
</#list>
course.ftl:
<#include "faculty.ftl" parse=true>
<#include "student.ftl" parse=true>
<#macro newCourse course indent>
<#local padding = ""?left_pad(4*indent)/>
${padding}course:
${padding} code: ${course.code}
${padding} name: ${course.name}
<#newFaculty code = "${course.faculty.code}" name = "${course.faculty.name}" indent = indent + 1/>
${padding} students:
<#list course.students as student>
<#newStudent code = "${student.code}" name = "${student.name}" indent = indent + 2/>
</#list>
</#macro>
faculty.ftl:
<#macro newFaculty code name indent>
<#local padding = ""?left_pad(4*indent)/>
${padding}faculty:
${padding} code: ${code}
${padding} name: ${name}
</#macro>
student.ftl:
<#macro newStudent code name indent>
<#local padding = ""?left_pad(4*indent)/>
${padding}- student:
${padding} code: ${code}
${padding} name: ${name}
</#macro>
pojos:
class Course {
private String code;
private String name;
private Faculty faculty;
private List<Student> students = new ArrayList<>();
}
class Faculty {
private String code;
private String name;
}
class Student {
private String code;
private String name;
}

Related

Visual Studio Code Snippet - Change Literal Name?

I am trying to create a snippet that creates a field and a property for that field.
I want to use pascalCase for the field and CamelCase for the property, I want the snippet to do this automatically. I can do it if I create two different literals and manually write the name for the field and the name for the property, is there a way to automatically "create" the name for the property with capital first letter with the name taken from the name of the field?
This is my current snippet code:
<CodeSnippet Format="1.0.0">
<Header>
<Title>Property etc</Title>
<Shortcut>fprop</Shortcut>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<Default>int</Default>
</Literal>
<Literal>
<ID>name</ID>
<Default>propertyName</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ $name$;
public $type$ $name$ <<<<--- this $name$ must have its first letter capitalized in the final result
{
get
{
return this.$name$;
}
set
{
this.$name$ = value;
}
}
$end$]]>
</Code>
</Snippet>
This is the expected result:
private int propertyName;
public int PropertyName
{
get
{
return this.propertyName;
}
set
{
this.propertyName = value;
}
}
If I use a different name for the field, for example "someFieldName" the name for the property should automatically become SomeFieldName (at the time of the snippet creation of course)
I know that I can do this if I use two separate Literals, but then I have to manually write the name for the field and then write the name for the property aswell, I want to avoid writing the property name manually.
It can be done with ReSharper.
Using Template Explorer ("Extensions->ReSharper->Tools->Templates Explorer...")
create new template
private $TYPE$ $FIELD_NAME$;
public $TYPE$ $PROPERTY_NAME$
{
get
{
return this.$FIELD_NAME$;
}
set
{
this.$FIELD_NAME$ = value;
}
}
Template settings:
uncheck Reformat
Parameters settings:
PROPERTY_Name set as first element, Editable, Suggest name for a variable
TYPE set as second element, MO-2, Suggest type for a new variable
FIELD_NAME set as third element, Not Editable, Value of another variable with first character in lower case and select PROPERTY_NAME

FreeMarker: Enumeration of Root

I am looking to enumerate the root object in FTL (freeMarker template language). for all hash arrays, and all sequence arrays, and all standard key value objects.
I need output in JSON format.
I have found the posted code for JSON enumeration:
http://ericbrandel.com/2013/03/28/freemarker-container-to-json/
<#macro objectToJsonMacro object>
<#compress single_line=true>
<#if object?is_hash || object?is_hash_ex>
<#assign first="true">
{
<#list object?keys as key>
<#if first="false">,</#if>
<#assign value><#objectToJsonMacro object=object<key> /></#assign>
"${key}" : ${value?trim}
<#assign first="false">
</#list>
}
<#elseif object?is_enumerable>
<#assign first="true">
[
<#list object as item>
<#if first="false">,</#if>
<#assign value><#objectToJsonMacro object=item /></#assign>
${value?trim}
<#assign first="false">
</#list>
]
<#else>
"${object?trim}"
</#if>
</#compress>
</#macro>
But the code does NOT WORK (throws an error) in the latest edition of FreeMarker.
I would appreciate any help that anyone can provide. Also, I don't see a way to "get" the root object.
Thanks,
Dan Linstedt
I solved this by adding a public method to my root object which JSON encodes itself. I used Gson to marshal the object.
/**
* #return JSON serialized version of this object.
*/
public String toJson()
{
GsonBuilder gsonBuilder = new GsonBuilder();
//gsonBuilder.setPrettyPrinting();
//gsonBuilder.disableHtmlEscaping();
// .. and whatever else you need Gson to do
return gsonBuilder.create().toJson(this);
}
I didn't need it in FreeMarker, but you could access it like:
${toJson()}

Freemarker parse a String as Json

Probably it's not possible,
but I would like to transform a json string in a map with freemarker
ex:
<#assign test = "{\"foo\":\"bar\", \"f\":4, \"text\":\"bla bla\"}">
and be able to get the text key from this string
Use ?eval_json (requires FreeMarker 2.3.31):
<#-- Using '...' instead of "..." for convenience: no need for \" escapes this way. -->
<#assign test = '{"foo":"bar", "f":4, "text":"bla bla"}'>
<#assign m = test?eval_json>
${m.foo} <#-- prints: bar -->
<#-- Dump the whole map: -->
<#list m as k, v>
${k} => ${v}
</#list>
Before 2.3.31, ?eval was popular for this purpose, but that actually expects FreeMarker expressions. That's a problem because it doesn't support null, or \uXXXX escapes (so parsing of such JSON will fail). Also it can be a security problem, because it supports accessing variables, and calling methods/functions, while JSON doesn't.
freemarker.sourceforge.net/docs/pgui_datamodel_method.html
in code:
// a class to parse Json, just add this method to your rendered template data
// with data.put("JsonParser", new FreemarkerJsonParser());
// or in shared variables http://freemarker.sourceforge.net/docs/pgui_config_sharedvariables.html
public class FreemarkerJsonParser implements TemplateMethodModel{
#Override
public Object exec(List args) throws TemplateModelException {
return new Gson().fromJson(s, new TypeToken<Map<String, String>>() {}.getType());((String) args.get(0));
}
}
in the template:
<#assign map = JsonParser("{\"foo\":\"bar\", \"f\":4, \"text\":\"bla bla\"}")>
${map.text}
Sounds like you need to define/implement a template that reads JSON.

NullReferenceException while Reading XML File with Linq (C# 4.0)

I'm completely stumped, I've used very similar code before and it worked perfectly, the XML in this was written by a separate method in this program and I checked it against it and it looked fine
That's the code for parsing the XML file
UserType CurrentUser = new UserType();
XDocument UserDoc = XDocument.Load(Path2UserFile);
XElement UserRoot = UserDoc.Element("User");
CurrentUser.User_ID = int.Parse(UserDoc.Element("User_ID").Value);
CurrentUser.Full_Name = UserDoc.Element("Full_Name").Value;
CurrentUser.Gender = UserDoc.Element("Gender").Value;
CurrentUser.BirthDate = DateTime.Parse(UserDoc.Element("Birthdate").Value);
CurrentUser.PersonType = int.Parse(UserDoc.Element("PersonType").Value);
CurrentUser.Username = UserDoc.Element("Username").Value;
CurrentUser.Password = UserDoc.Element("Password").Value;
CurrentUser.Email_Address = UserDoc.Element("Email_Address").Value;
The Path2UserFile points to the correct file as well, and I had it write out the full path.
It has a NullReferenceException whenever it tries to parse the contents of any of the elements
The XML File follows this format
<User>
<User_ID>11</User_ID>
<Full_Name>Sample User</Full_Name>
<Gender>Male</Gender>
<BirthDate>12/12/2010 12:00:00 AM</BirthDate>
<PersonType>2</PersonType>
<Username>Sample User</Username>
<Password>sample123</Password>
<Email_adddress>sampleuser#gmail.com</Email_adddress>
</User>
The UserType class looks like this
class UserType
{
public int User_ID = 0;
public string Full_Name = string.Empty;
public string Gender = string.Empty;
public DateTime BirthDate;
public int PersonType = 0;
public string Username = string.Empty;
public string Password = string.Empty;
public string Email_Address = string.Empty;
}
I have no clue as to what's wrong, any help would be very much appreciated
Change all the UserDoc references to UserRoot (the ones after the UserRoot declaration). Since the object is an XDocument rather than an XElement you need to operate at that level. Otherwise you can refer to UserDoc.Root.Element(...) instead but that's lengthier.
UserType CurrentUser = new UserType();
XDocument UserDoc = XDocument.Load(Path2UserFile);
XElement UserRoot = UserDoc.Root;
CurrentUser.User_ID = int.Parse(UserRoot.Element("User_ID").Value);
CurrentUser.Full_Name = UserRoot.Element("Full_Name").Value;
CurrentUser.Gender = UserRoot.Element("Gender").Value;
CurrentUser.BirthDate = DateTime.Parse(UserRoot.Element("BirthDate").Value);
CurrentUser.PersonType = int.Parse(UserRoot.Element("PersonType").Value);
CurrentUser.Username = UserRoot.Element("Username").Value;
CurrentUser.Password = UserRoot.Element("Password").Value;
CurrentUser.Email_Address = UserRoot.Element("Email_address").Value;
Also, be aware of your case. Use BirthDate instead of Birthdate (capital "D" to match your XML). Similarly, it's Email_address not Email_Address (lowercase "a") and your XML has 3 D's in "address" (spelling mistake).

In freemarker is it possible to check to see if a file exists before including it?

We are trying to build a system in freemarker where extension files can be optionally added to replace blocks of the standard template.
We have gotten to this point
<#attempt>
<#include "extension.ftl">
<#recover>
Standard output
</#attempt>
So - if the extension.ftl file exists it will be used otherwise the part inside of the recover block is output.
The problem with this is that freemarker always logs the error that caused the recover block to trigger.
So we need one of two things:
Don't call the include if the file doesn't exist - thus the need to check for file existence.
-OR-
A way to prevent the logging of the error inside the recover block without changing the logging to prevent ALL freemarker errors from showing up.
easier solution would be:
<#attempt>
<#import xyz.ftl>
your_code_here
<#recover>
</#attempt>
We've written a custom macro which solves this for us. In early testing, it works well. To include it, add something like this (where mm is a Spring ModelMap):
mm.addAttribute(IncludeIfExistsMacro.MACRO_NAME, new IncludeIfExistsMacro());
import java.io.IOException;
import java.util.Map;
import org.apache.commons.io.FilenameUtils;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateLoader;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* This macro optionally includes the template given by path. If the template isn't found, it doesn't
* throw an exception; instead it renders the nested content (if any).
*
* For example:
* <#include_if_exists path="module/${behavior}.ftl">
* <#include "default.ftl">
* </#include_if_exists>
*
* #param path the path of the template to be included if it exists
* #param nested (optional) body could be include directive or any other block of code
*/
public class IncludeIfExistsMacro implements TemplateDirectiveModel {
private static final String PATH_PARAM = "path";
public static final String MACRO_NAME = "include_if_exists";
#Override
public void execute(Environment environment, Map map, TemplateModel[] templateModel,
TemplateDirectiveBody directiveBody) throws TemplateException, IOException {
if (! map.containsKey(PATH_PARAM)) {
throw new RuntimeException("missing required parameter '" + PATH_PARAM + "' for macro " + MACRO_NAME);
}
// get the current template's parent directory to use when searching for relative paths
final String currentTemplateName = environment.getTemplate().getName();
final String currentTemplateDir = FilenameUtils.getPath(currentTemplateName);
// look up the path relative to the current working directory (this also works for absolute paths)
final String path = map.get(PATH_PARAM).toString();
final String fullTemplatePath = TemplateCache.getFullTemplatePath(environment, currentTemplateDir, path);
TemplateLoader templateLoader = environment.getConfiguration().getTemplateLoader();
if (templateLoader.findTemplateSource(fullTemplatePath) != null) {
// include the template for the path, if it's found
environment.include(environment.getTemplateForInclusion(fullTemplatePath, null, true));
} else {
// otherwise render the nested content, if there is any
if (directiveBody != null) {
directiveBody.render(environment.getOut());
}
}
}
}
I had this exact need as well but I didn't want to use FreeMarker's ObjectConstructor (it felt too much like a scriptlet for my taste).
I wrote a custom FileTemplateLoader:
public class CustomFileTemplateLoader
extends FileTemplateLoader {
private static final String STUB_FTL = "/tools/empty.ftl";
public CustomFileTemplateLoader(File baseDir) throws IOException {
super(baseDir);
}
#Override
public Object findTemplateSource(String name) throws IOException {
Object result = null;
if (name.startsWith("optional:")) {
result = super.findTemplateSource(name.replace("optional:", ""));
if (result == null) {
result = super.findTemplateSource(STUB_FTL);
}
}
if (result == null) {
result = super.findTemplateSource(name);
}
return result;
}
}
And my corresponding FreeMarker macro:
<#macro optional_include name>
<#include "/optional:" + name>
</#macro>
An empty FTL file was required (/tools/empty.ftl) which just contains a comment explaining its existence.
The result is that an "optional" include will just include this empty FTL if the requested FTL cannot be found.
You can use also use Java method to check file exist or not.
Java Method-
public static boolean checkFileExistance(String filePath){
File tmpDir = new File(filePath);
boolean exists = tmpDir.exists();
return exists;
}
Freemarker Code-
<#assign fileExists = (Static["ClassName"].checkFileExistance("Filename"))?c/>
<#if fileExists = "true">
<#include "/home/demo.ftl"/>
<#else>
<#include "/home/index.ftl">
</#if>
Try this to get the base path:
<#assign objectConstructor = "freemarker.template.utility.ObjectConstructor"?new()>
<#assign file = objectConstructor("java.io.File","")>
<#assign path = file.getAbsolutePath()>
<script type="text/javascript">
alert("${path?string}");
</script>
Then this to walk the directory structure:
<#assign objectConstructor = "freemarker.template.utility.ObjectConstructor"?new()>
<#assign file = objectConstructor("java.io.File","target/test.ftl")>
<#assign exist = file.exists()>
<script type="text/javascript">
alert("${exist?string}");
</script>

Resources