【问题标题】:Is it possible to "pre-process" Freemarker templates?是否可以“预处理” Freemarker 模板?
【发布时间】:2018-04-18 23:26:54
【问题描述】:

考虑以下模板:

<#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 做到这一点?

【问题讨论】:

    标签: java freemarker


    【解决方案1】:

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

    /**
     * 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);
        }
    }
    

    【讨论】:

      【解决方案2】:

      FreeMarker 不支持这样做(仅解析模板的某些部分)。您可以做的是使用您自己的解析器对模板进行预处理。这通过使用您自己的TemplateLoader 实现来支持,该实现委托给另一个TemplateLoader(原始的)并过滤内容。因此,您可以在第一次需要模板时即时应用转换,结果将被缓存(在 FreeMarker 的标准模板缓存中)。我建议使用您自己的语法(例如&lt;%include '...'&gt;),这样每个人都会看到那里发生了一些特别的事情。

      【讨论】:

        猜你喜欢
        • 2012-12-26
        • 2015-06-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-29
        • 2018-05-02
        • 2013-04-11
        • 1970-01-01
        相关资源
        最近更新 更多