Sunday 5 July 2020

FreeMarker: macro: Define Custom Directive

‘macro’ is used to define custom directives.

 

Syntax

<#macro name param1 param2 ... paramN>
  ...
  <#nested loopvar1, loopvar2, ..., loopvarN>
  ...
  <#return>
  ...
</#macro>

name’: Name of Macro variable.

param1, param2, ...:  The name of the local variables store the parameter values

paramN: the last parameter may optionally have 3 trailing dots (...), which indicates that the macro takes a variable number of parameters

loopvar1, loopvar2, ...etc.: Optional. The values of loop variables that the nested directive wants to create for the nested content.

 

Example

<#macro quotations>
	- Be yourself; everyone else is already taken.
	- Be the change that you wish to see in the world.
	- Live as if you were to die tomorrow. 
</#macro>

‘macro’ directive itself does not print anything. Information between <#macro quotations> and <#macro> will be executed only when you use the variable ‘quotations’ as a directive.

 

How to use user-defined directive?

Use @symbol to call the directive.

 

Example

<@quotations></@quotations>

 

Find the below working application.

 

Step 1: Define 'macroDef1.ftl' file under src/main/resources/templates folder.

 

macroDef1.ftl

<#macro quotations>
	- Be yourself; everyone else is already taken.
	- Be the change that you wish to see in the world.
	- Live as if you were to die tomorrow. 
</#macro>

Printing Quotations
<@quotations></@quotations>

Printing Quotations Again
<@quotations></@quotations>

Step 2: Define ‘FreeMarkerUtil’ class that takes model class and template file as input and merge them.

 

FreeMarkerUtil.java

package com.sample.app.util;

import java.io.StringWriter;
import java.util.Locale;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;

public class FreeMarkerUtil {

	private static final Configuration FREE_MARKER_CONFIGURATION = new Configuration(Configuration.VERSION_2_3_30);

	static {
		FREE_MARKER_CONFIGURATION.setClassForTemplateLoading(FreeMarkerUtil.class, "/templates/");
		FREE_MARKER_CONFIGURATION.setDefaultEncoding("UTF-8");
		FREE_MARKER_CONFIGURATION.setLocale(Locale.US);
		FREE_MARKER_CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
		FREE_MARKER_CONFIGURATION.setFallbackOnNullLoopVariable(false);
	}

	public static StringWriter mergeModelAndTemplate(Object modelObject, String ftlFile) throws Exception {
		StringWriter stringWriter = new StringWriter();

		Template template = FREE_MARKER_CONFIGURATION.getTemplate(ftlFile);

		template.process(modelObject, stringWriter);

		return stringWriter;
	}

}

Step 3: Define MacroDef1Populator.

 

MacroDef1Populator.java

package com.sample.app;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import com.sample.app.util.FreeMarkerUtil;

public class MacroDef1Populator {
    public static void main(String args[]) throws Exception {

        Map<String, Object> modelObject = new HashMap<String, Object>();

        StringWriter stringWriter = FreeMarkerUtil.mergeModelAndTemplate(modelObject, "macroDef1.ftl");
        System.out.println(stringWriter.toString().trim());

    }
}

Run the application, you will see below messages in the console.

Printing Quotations
    - Be yourself; everyone else is already taken.
    - Be the change that you wish to see in the world.
    - Live as if you were to die tomorrow. 

Printing Quotations Again
    - Be yourself; everyone else is already taken.
    - Be the change that you wish to see in the world.
    - Live as if you were to die tomorrow.

Passing Parameters to a macro

<#macro sum a b>

    Sum of ${a} and ${b} is ${a+b}

</#macro>

 

You can call the sum macro with parameters 10 and 20 like below.

<@sum 10 20></@sum>

 

You can even call the macro with variable names.

 

For example,

<#macro employee id name age>

    Hi ${name}, your id is ${id} and you are ${age} years old

</#macro>

 

You can call the employee directive like below.

<@employee id=76 name="Ram" age=32></@employee>

 

If you use variable names, then the order of variables doesn’t matter.

 

macroParameters.ftl

<#macro sum a b>
    Sum of ${a} and ${b} is ${a+b}
</#macro>

<@sum 10 20></@sum>
<@sum 20 30></@sum>
<@sum 30 40></@sum>

<#macro employee id name age>
    Hi ${name}, your id is ${id} and you are ${age} years old
</#macro>

<@employee 123 "Krishna" 31></@employee>
<@employee id=76 name="Ram" age=32></@employee>

MacroParametersPopulator.java

package com.sample.app;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import com.sample.app.util.FreeMarkerUtil;

public class MacroParametersPopulator {
    public static void main(String args[]) throws Exception {

        Map<String, Object> modelObject = new HashMap<String, Object>();

        StringWriter stringWriter = FreeMarkerUtil.mergeModelAndTemplate(modelObject, "macroParameters.ftl");
        System.out.println(stringWriter.toString());

    }
}

Output

    Sum of 10 and 20 is 30
    Sum of 20 and 30 is 50
    Sum of 30 and 40 is 70


    Hi Krishna, your id is 123 and you are 31 years old
    Hi Ram, your id is 76 and you are 32 years old

Providing default value to variables in a macro

For example, I defined sum macro like below.

 

<#macro sum a=10 b=20>

    Sum of ${a} and ${b} is ${a+b}

</#macro>

 

If you do not pass any values to the macro sum, then 10 is taken as the default value for the variable a, and 20 is taken as the default value for the variable b.

 

<@sum></@sum>: Call the macro sum. Since we do not pass any values a is assigned with value 10 and b is assigned with value 20.

 

<@sum 19></@sum>: Since I passed 19 as an argument, 'a' will be assigned with value 19, and 'b' will be assigned to the default value which is 20.

 

<@sum b=40></@sum>: Since I do not pass any value to the variable 'a', it will be assigned with default value 10.

 

macroDefaultValues.ftl

<#macro sum a=10 b=20>
    Sum of ${a} and ${b} is ${a+b}
</#macro>

<@sum></@sum>
<@sum 19></@sum>
<@sum b=40></@sum>

MacroDefaultValuePopulator.java

package com.sample.app;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import com.sample.app.util.FreeMarkerUtil;

public class MacroDefaultValuePopulator {
    public static void main(String args[]) throws Exception {

        Map<String, Object> modelObject = new HashMap<String, Object>();

        StringWriter stringWriter = FreeMarkerUtil.mergeModelAndTemplate(modelObject, "macroDefaultValues.ftl");
        System.out.println(stringWriter.toString());

    }
}

Output

    Sum of 10 and 20 is 30
    Sum of 19 and 20 is 39
    Sum of 10 and 40 is 50

Nested content

We can pass content to a custom directive and it can be accessed using ‘nested’ directive.


For example, I defined a macro as below.

 

<#macro welcome name>

    <#nested> ${name}

</#macro>

 

<@welcome "Krishna">Good Morning</@welcome>

Prints "Good Morning Krishna"

 

<@welcome "Narasimha">Good Afternoon</@welcome>

Prints "Good Afternoon Narasimha"

 

<@welcome "Loopa">Good Evening</@welcome>

Prints "Good Evening Loopa"

 

Find the below working application.

 

macroNestedContent.ftl

<#macro welcome name>
    <#nested> ${name}
</#macro>

<@welcome "Krishna">Good Morning</@welcome>
<@welcome "Narasimha">Good Afternoon</@welcome>
<@welcome "Loopa">Good Evening</@welcome>

MacroNestedContentPopulator.java

package com.sample.app;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import com.sample.app.util.FreeMarkerUtil;

public class MacroNestedContentPopulator {
    public static void main(String args[]) throws Exception {

        Map<String, Object> modelObject = new HashMap<String, Object>();

        StringWriter stringWriter = FreeMarkerUtil.mergeModelAndTemplate(modelObject, "macroNestedContent.ftl");
        System.out.println(stringWriter.toString());

    }
}

Output

    Good Morning Krishna

    Good Afternoon Narasimha

    Good Evening Loopa

Note

a.   Local variables of macro do not visible in nested content.



Previous                                                    Next                                                    Home

No comments:

Post a Comment