CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-freemarker--freemarker

Apache FreeMarker is a template engine: a Java library to generate text output based on templates and changing data.

Pending
Overview
Eval results
Files

output-formats.mddocs/

Output Formats and Escaping

FreeMarker's output format system provides automatic escaping and content-type-aware processing to prevent security vulnerabilities like XSS attacks while ensuring proper content formatting.

Core Output Format Classes

Base Output Format

abstract class OutputFormat {
  // Format identification
  abstract String getName();
  abstract String getMimeType();
  abstract boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);
  
  // Escaping capabilities
  abstract boolean isAutoEscapedByDefault();
  
  // Format compatibility
  boolean isMarkupFormat();
  boolean isSourceCodeFormat();
}

Markup Output Format Base

abstract class MarkupOutputFormat<MO extends TemplateMarkupOutputModel<MO>> extends OutputFormat {
  // Markup-specific methods
  abstract MO fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;
  abstract MO fromMarkup(String markupText) throws TemplateModelException;
  abstract MO getEmpty();
  
  // Markup concatenation
  abstract MO concat(MO... outputModels) throws TemplateModelException;
  
  // Format information
  boolean isAutoEscapedByDefault();
  boolean isMarkupFormat();
}

// Common markup format base
abstract class CommonMarkupOutputFormat<MO extends CommonTemplateMarkupOutputModel<MO>> 
    extends MarkupOutputFormat<MO> {
  
  // Common escaping functionality
  abstract String escapePlainText(String plainTextContent);
  abstract boolean isLegacyBuiltInBypassed(String builtInName);
}

Built-in Output Formats

HTML Output Format

class HTMLOutputFormat extends CommonMarkupOutputFormat<TemplateHTMLOutputModel> {
  // Singleton instance
  static final HTMLOutputFormat INSTANCE = new HTMLOutputFormat();
  
  // Format identification
  String getName();
  String getMimeType();
  
  // HTML-specific escaping
  String escapePlainText(String plainTextContent);
  TemplateHTMLOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;
  TemplateHTMLOutputModel fromMarkup(String markupText) throws TemplateModelException;
  TemplateHTMLOutputModel getEmpty();
  
  // HTML model creation
  TemplateHTMLOutputModel fromPlainTextByEscaping(String textToEsc);
  TemplateHTMLOutputModel fromMarkup(String markupText);
}

// HTML output model
interface TemplateHTMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateHTMLOutputModel> {
  String getMarkupString() throws TemplateModelException;
}

XML Output Format

class XMLOutputFormat extends CommonMarkupOutputFormat<TemplateXMLOutputModel> {
  // Singleton instance
  static final XMLOutputFormat INSTANCE = new XMLOutputFormat();
  
  // Format identification
  String getName();
  String getMimeType();
  
  // XML-specific escaping
  String escapePlainText(String plainTextContent);
  TemplateXMLOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;
  TemplateXMLOutputModel fromMarkup(String markupText) throws TemplateModelException;
  TemplateXMLOutputModel getEmpty();
}

// XML output model
interface TemplateXMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXMLOutputModel> {
  String getMarkupString() throws TemplateModelException;
}

XHTML Output Format

class XHTMLOutputFormat extends HTMLOutputFormat {
  // Singleton instance
  static final XHTMLOutputFormat INSTANCE = new XHTMLOutputFormat();
  
  // Format identification (inherits most from HTMLOutputFormat)
  String getName();
  String getMimeType();
}

RTF Output Format

class RTFOutputFormat extends CommonMarkupOutputFormat<TemplateRTFOutputModel> {
  // Singleton instance
  static final RTFOutputFormat INSTANCE = new RTFOutputFormat();
  
  // Format identification
  String getName();
  String getMimeType();
  
  // RTF-specific escaping
  String escapePlainText(String plainTextContent);
  TemplateRTFOutputModel fromPlainTextByEscaping(String textToEsc) throws TemplateModelException;
  TemplateRTFOutputModel fromMarkup(String markupText) throws TemplateModelException;
  TemplateRTFOutputModel getEmpty();
}

Plain Text Output Format

class PlainTextOutputFormat extends OutputFormat {
  // Singleton instance
  static final PlainTextOutputFormat INSTANCE = new PlainTextOutputFormat();
  
  // Format identification
  String getName();
  String getMimeType();
  boolean isAutoEscapedByDefault();
  boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);
}

Undefined Output Format

class UndefinedOutputFormat extends OutputFormat {
  // Singleton instance
  static final UndefinedOutputFormat INSTANCE = new UndefinedOutputFormat();
  
  // Format identification
  String getName();
  String getMimeType();
  boolean isAutoEscapedByDefault();
  boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat);
}

Configuration and Usage

Output Format Configuration

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

// Set default output format
cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);

// Configure auto-escaping policy
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

// Register custom output formats
Set<OutputFormat> customFormats = new HashSet<>();
customFormats.add(new CustomJSONOutputFormat());
cfg.setRegisteredCustomOutputFormats(customFormats);

// Recognize standard file extensions for automatic format selection
cfg.setRecognizeStandardFileExtensions(true);

Template-Specific Output Format

// Configure specific output format for certain templates
TemplateConfiguration htmlConfig = new TemplateConfiguration();
htmlConfig.setOutputFormat(HTMLOutputFormat.INSTANCE);
htmlConfig.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

cfg.setTemplateConfigurations(
    new ConditionalTemplateConfigurationFactory(
        new FileExtensionMatcher("html"),
        new FirstMatchTemplateConfigurationFactory(htmlConfig)
    )
);

// Plain text for email templates
TemplateConfiguration textConfig = new TemplateConfiguration();
textConfig.setOutputFormat(PlainTextOutputFormat.INSTANCE);

cfg.setTemplateConfigurations(
    new ConditionalTemplateConfigurationFactory(
        new PathGlobMatcher("email/**"),
        new FirstMatchTemplateConfigurationFactory(textConfig)
    )
);

Auto-Escaping Policies

// Auto-escaping policy constants in Configuration
static final int ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY = 0;
static final int ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY = 1; 
static final int DISABLE_AUTO_ESCAPING_POLICY = 2;

Auto-Escaping Configuration Examples

Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

// Enable auto-escaping only for formats where it's default (like HTML)
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

// Enable auto-escaping for all markup formats that support it
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY);

// Disable auto-escaping completely (not recommended for security)
cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);

Template Markup Output Models

Common Template Markup Output Model

interface CommonTemplateMarkupOutputModel<MO extends CommonTemplateMarkupOutputModel<MO>> 
    extends TemplateMarkupOutputModel<MO> {
  
  String getMarkupString() throws TemplateModelException;
  MO concat(MO... outputModels) throws TemplateModelException;
}

// Base markup output model
interface TemplateMarkupOutputModel<MO extends TemplateMarkupOutputModel<MO>> 
    extends TemplateModel {
  
  MO getOutputFormat();
}

Specific Output Models

// HTML output model implementation
class TemplateHTMLOutputModelImpl implements TemplateHTMLOutputModel {
  TemplateHTMLOutputModelImpl(String markupContent, HTMLOutputFormat outputFormat);
  String getMarkupString() throws TemplateModelException;
  TemplateHTMLOutputModel concat(TemplateHTMLOutputModel... outputModels) throws TemplateModelException;
  HTMLOutputFormat getOutputFormat();
}

// XML output model implementation  
class TemplateXMLOutputModelImpl implements TemplateXMLOutputModel {
  TemplateXMLOutputModelImpl(String markupContent, XMLOutputFormat outputFormat);
  String getMarkupString() throws TemplateModelException;
  TemplateXMLOutputModel concat(TemplateXMLOutputModel... outputModels) throws TemplateModelException;
  XMLOutputFormat getOutputFormat();
}

Custom Output Formats

Creating Custom Output Formats

public class JSONOutputFormat extends OutputFormat {
    public static final JSONOutputFormat INSTANCE = new JSONOutputFormat();
    
    private JSONOutputFormat() {
        // Private constructor for singleton
    }
    
    @Override
    public String getName() {
        return "JSON";
    }
    
    @Override
    public String getMimeType() {
        return "application/json";
    }
    
    @Override
    public boolean isAutoEscapedByDefault() {
        return true;
    }
    
    @Override
    public boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat) {
        return otherOutputFormat instanceof JSONOutputFormat ||
               otherOutputFormat instanceof PlainTextOutputFormat;
    }
}

// JSON markup output format with escaping
public class JSONMarkupOutputFormat extends MarkupOutputFormat<TemplateJSONOutputModel> {
    public static final JSONMarkupOutputFormat INSTANCE = new JSONMarkupOutputFormat();
    
    @Override
    public TemplateJSONOutputModel fromPlainTextByEscaping(String textToEsc) 
        throws TemplateModelException {
        return new TemplateJSONOutputModelImpl(escapeJSON(textToEsc), this);
    }
    
    @Override
    public TemplateJSONOutputModel fromMarkup(String markupText) 
        throws TemplateModelException {
        return new TemplateJSONOutputModelImpl(markupText, this);
    }
    
    @Override
    public TemplateJSONOutputModel getEmpty() {
        return new TemplateJSONOutputModelImpl("", this);
    }
    
    @Override
    public TemplateJSONOutputModel concat(TemplateJSONOutputModel... outputModels) 
        throws TemplateModelException {
        StringBuilder sb = new StringBuilder();
        for (TemplateJSONOutputModel model : outputModels) {
            sb.append(model.getMarkupString());
        }
        return new TemplateJSONOutputModelImpl(sb.toString(), this);
    }
    
    private String escapeJSON(String text) {
        return text.replace("\\", "\\\\")
                  .replace("\"", "\\\"")
                  .replace("\n", "\\n")
                  .replace("\r", "\\r")
                  .replace("\t", "\\t")
                  .replace("\b", "\\b")
                  .replace("\f", "\\f");
    }
    
    @Override
    public String getName() {
        return "JSON";
    }
    
    @Override
    public String getMimeType() {
        return "application/json";
    }
    
    @Override
    public boolean isAutoEscapedByDefault() {
        return true;
    }
    
    @Override
    public boolean isOutputFormatMixingAllowed(OutputFormat otherOutputFormat) {
        return otherOutputFormat instanceof JSONMarkupOutputFormat ||
               otherOutputFormat instanceof PlainTextOutputFormat;
    }
}

Template Usage Examples

Basic Auto-Escaping

<!-- Template with HTML output format -->
<html>
<head>
    <title>${pageTitle}</title>
</head>
<body>
    <h1>${heading}</h1>
    <p>${userComment}</p>  <!-- Automatically escaped for HTML -->
    <p>${safeHtmlContent?no_esc}</p>  <!-- Bypass escaping when safe -->
</body>
</html>

Format-Specific Escaping

// Data model with potentially dangerous content
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("userInput", "<script>alert('XSS')</script>");
dataModel.put("xmlContent", "<item>Value & \"quoted\"</item>");

// HTML template - userInput will be escaped
// Output: &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;

// XML template - xmlContent will be escaped for XML
// Output: &lt;item&gt;Value &amp; &quot;quoted&quot;&lt;/item&gt;

// Plain text template - no escaping applied
// Output: <script>alert('XSS')</script>

Mixed Output Formats

<!-- Main HTML template -->
<html>
<body>
    <script type="application/json">
        <#-- Switch to JSON output format for this section -->
        <#outputformat "JSON">
        {
            "userData": "${userData}",  <!-- JSON-escaped -->
            "message": "${message}"     <!-- JSON-escaped -->
        }
        </#outputformat>
    </script>
    
    <div class="content">
        ${htmlContent}  <!-- HTML-escaped -->
    </div>
</body>
</html>

Built-in Functions for Output Formats

Format-Related Built-ins

<!-- Check current output format -->
<#if .output_format == "HTML">
    <!-- HTML-specific content -->
</#if>

<!-- Get output format name -->
Current format: ${.output_format}

<!-- Force escaping regardless of auto-escaping setting -->
${dangerousContent?esc}

<!-- Bypass escaping (use with caution) -->
${safeMarkup?no_esc}

<!-- Convert to different format -->
${htmlContent?markup_string}  <!-- Get raw markup string -->

<!-- Check if content is already escaped -->
<#if content?is_markup_output>
    Content is already escaped
</#if>

Advanced Format Handling

<!-- Conditional escaping based on format -->
<#if .output_format?starts_with("HTML")>
    ${content?html}
<#elseif .output_format == "XML">  
    ${content?xml}
<#elseif .output_format == "RTF">
    ${content?rtf}
<#else>
    ${content}
</#if>

<!-- Format-aware concatenation -->
<#assign combined = markup1 + markup2>  <!-- Properly combines markup -->

<!-- Convert between formats -->
<#outputformat "XML">
    <#assign xmlEscaped = htmlContent?string>
</#outputformat>

Security Considerations

XSS Prevention

// Proper configuration for web applications
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);

// Template usage - automatic escaping prevents XSS
// ${userInput} -> automatically HTML-escaped
// ${trustedHtml?no_esc} -> bypasses escaping (use with extreme caution)

Content Security Policies

// Custom output format with CSP-aware escaping
public class CSPAwareHTMLOutputFormat extends HTMLOutputFormat {
    @Override
    public String escapePlainText(String plainTextContent) {
        // Enhanced escaping for Content Security Policy compliance
        return super.escapePlainText(plainTextContent)
                   .replace("javascript:", "blocked:")
                   .replace("data:", "blocked:")
                   .replace("vbscript:", "blocked:");
    }
}

Trusted Content Handling

// Wrapper for trusted content that should not be escaped
public class TrustedContent implements TemplateScalarModel, TemplateMarkupOutputModel {
    private final String content;
    private final OutputFormat outputFormat;
    
    public TrustedContent(String content, OutputFormat outputFormat) {
        this.content = content;
        this.outputFormat = outputFormat;
    }
    
    @Override
    public String getAsString() throws TemplateModelException {
        return content;
    }
    
    @Override
    public OutputFormat getOutputFormat() {
        return outputFormat;
    }
}

// Usage
dataModel.put("trustedHtml", new TrustedContent("<b>Safe HTML</b>", HTMLOutputFormat.INSTANCE));

Performance Considerations

Output Format Selection Performance

// Pre-configure output formats for better performance
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);

// Use template configurations for automatic format selection
cfg.setRecognizeStandardFileExtensions(true);  // Automatic based on file extension

// Pre-register custom formats
Set<OutputFormat> customFormats = new HashSet<>();
customFormats.add(CustomJSONOutputFormat.INSTANCE);
customFormats.add(CustomXMLOutputFormat.INSTANCE);
cfg.setRegisteredCustomOutputFormats(customFormats);

Escaping Performance

// Efficient escaping for high-throughput applications
public class OptimizedHTMLOutputFormat extends HTMLOutputFormat {
    private static final char[] HTML_ESCAPE_CHARS = {'&', '<', '>', '"', '\''};
    
    @Override
    public String escapePlainText(String plainTextContent) {
        // Optimized escaping implementation
        if (plainTextContent == null || plainTextContent.isEmpty()) {
            return plainTextContent;
        }
        
        // Quick check if escaping is needed
        boolean needsEscaping = false;
        for (char c : HTML_ESCAPE_CHARS) {
            if (plainTextContent.indexOf(c) != -1) {
                needsEscaping = true;
                break;
            }
        }
        
        if (!needsEscaping) {
            return plainTextContent;
        }
        
        // Perform escaping only when necessary
        return super.escapePlainText(plainTextContent);
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-freemarker--freemarker

docs

caching-loading.md

core-processing.md

exception-handling.md

extensions.md

index.md

object-wrapping.md

output-formats.md

template-models.md

tile.json