Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

freemarker

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:

  1. Don't call the include if the file doesn't exist - thus the need to check for file existence.

-OR-

  1. 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.
like image 925
harmanjd Avatar asked Apr 13 '10 15:04

harmanjd


2 Answers

easier solution would be:

<#attempt>
    <#import xyz.ftl>
    your_code_here
<#recover>
</#attempt>
like image 122
kecsolecso Avatar answered Sep 29 '22 20:09

kecsolecso


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());
            }
        }
    }
}
like image 33
anomolos Avatar answered Sep 29 '22 19:09

anomolos