是否可以“预处理”Freemarker模板?

时间:2017-11-06 18:04:05

标签: java freemarker

考虑以下模板:

<#include "../header.txt"/>
<#list items as item>
Item name is: ${item.name}<br/>
</#list>

header.txt包含的地方:

<html>
<head>
</head>
<body>

我想“预处理”此模板,以便得到的结果是:

<html>
<head>
</head>
<body>
<#list items as item>
Item name is: ${item.name}<br/>
</#list>

我希望能够扩展包含但解析变量。我怎么能用Freemarker做到这一点?

2 个答案:

答案 0 :(得分:1)

FreeMarker不支持这样做(仅解析模板的某些部分)。您可以做的是使用您自己的解析器预处理模板。通过使用您自己的TemplateLoader实施代表支持另一个TemplateLoader(原始版本)并过滤内容来支持这一点。因此,当第一次需要模板时,您可以即时应用转换,结果将被缓存(在FreeMarker的标准模板缓存中)。我建议使用你自己的语法(比如<%include '...'>),这样每个人都会看到那里有特别的东西。

答案 1 :(得分:0)

我最终不得不创建自己的扩展器。我希望它对某人有用:

/**
 * Utility class to perform Freemarker template expansion.
 * 
 * @author Chris Mepham
 */
public class FreemarkerTemplateExpander implements ApplicationAware {

    static final String INCLUDE_REGEX = "<#include \\\"\\S+\\\"\\/>";

    static final String PATH_REGEX = "\\\"\\S+\\\"";

    private ModuleStateHolder moduleStateHolder;

    /**
     * Takes the Freemarker template String input and
     * 
     * recursively expands all of the includes.
     * 
     * @param input The String value of a Freemarker template.
     * 
     * @return The expanded version of the Freemarker template.
     */
    public final String expand(String module, String path, String input) {

        Assert.notNull(module, "module cannot be null");
        Assert.notNull(path, "path cannot be null");
        Assert.notNull(input, "input cannot be null");

        if (!hasText(input)) return input;

        // See if there is an include
        int indexOfNextInclude = getIndexOfNextInclude(Pattern.compile(INCLUDE_REGEX), input);

        // If there is no include just return the input text
        if (indexOfNextInclude == -1) return input;

        StringBuffer buffer = new StringBuffer();

        // Otherwise, get all the text up to the next include and add it to buffer
        String prefix = input.substring(0, indexOfNextInclude);
        if (hasText(prefix)) buffer.append(prefix);

        // Then get the contents of the include as a String
        String includeContents = getIncludeContents(module, path, input);
        if (hasText(includeContents)) buffer.append(includeContents);

        // Then get all the text after the next include
        int includeLastCharacterIndex = indexOfNextInclude + matchRegexPattern(input, INCLUDE_REGEX).length();

        String suffix = input.substring(includeLastCharacterIndex + 1);

        buffer.append(suffix);

        input = buffer.toString();

        return expand(module, path, input);
    }

    final String getIncludeContents(String module, String path1, String input) {

        // Get next include file relative path
        String nextIncludePath = getNextIncludePath(input);
        String resourcePath = getResourcePath(nextIncludePath);

        // Get file name
        String filename = getFilename(nextIncludePath);

        // Get file contents here
        String path = "templates." + resourcePath;
        InputStream resource = ClasspathResourceUtils.getClassPathResource(path, filename, getClassLoader(module));

        StringWriter writer = new StringWriter();
        try {
            IOUtils.copy(resource, writer, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return writer.toString();
    }

    static final int getIndexOfNextInclude(Pattern pattern, String input) {

        Matcher matcher = pattern.matcher(input);

        return matcher.find() ? matcher.start() : -1;
    }

    private static final String getNextIncludePath(String input) {

        String include = matchRegexPattern(input, INCLUDE_REGEX);

        if (include == null) return null;

        String path = matchRegexPattern(include, PATH_REGEX);
        path = path.replace("\"", "");

        return path;
    }

    private static final String matchRegexPattern(String input, String regex) {

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);

        while(matcher.find()) {

            return matcher.group(0);
        }

        return null;
    }

    private String getResourcePath(String path) {

        if (!path.contains("/")) return path;

        String resourcePath = path.substring(path.indexOf("/") + 1, path.lastIndexOf("/"));

        return resourcePath;

    }

    private String getFilename(String path) {

        if (!path.contains("/")) return path;

        return path.substring(path.lastIndexOf("/") + 1);
    }
}