Total Pageviews

2020/10/10

[Apache Freemarker] custom number format example

Purpose
FreeMarker allows you to define your own number and date/time/datetime formats, and associate a name to them.

Here has an example to demonstrate how to convert amount from 2380 to NT$2,380.


How-To
1. Create custom number format class:
package com.cht.tool.filegenerator.ftl.custom;

import freemarker.core.Environment;
import freemarker.core.TemplateFormatUtil;
import freemarker.core.TemplateNumberFormat;
import freemarker.core.TemplateNumberFormatFactory;
import freemarker.core.TemplateValueFormatException;
import freemarker.core.UnformattableValueException;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.utility.NumberUtil;

import java.text.DecimalFormat;
import java.util.Locale;

/**
 * 將金額加上千分號,並註明新台幣 NT
 * <p>
 * 使用方式 ${total?string.@ntd},2380 會被轉換為 NT$2,380
 */
public class NTDTemplateNumberFormatFactory extends TemplateNumberFormatFactory {

    public static final NTDTemplateNumberFormatFactory INSTANCE = new NTDTemplateNumberFormatFactory();
    private static DecimalFormat ntdFormat = new DecimalFormat("NT$#,###");

    private NTDTemplateNumberFormatFactory() {
        // Defined to decrease visibility
    }

    @Override
    public TemplateNumberFormat get(String s, Locale locale, Environment environment)
            throws TemplateValueFormatException {
        TemplateFormatUtil.checkHasNoParameters(s);
        return NTDTemplateStringFormat.INSTANCE;
    }

    private static class NTDTemplateStringFormat extends TemplateNumberFormat {

        private static final NTDTemplateStringFormat INSTANCE = new NTDTemplateStringFormat();

        private NTDTemplateStringFormat() {
            // Defined to decrease visibility
        }

        /**
         * 2380 會被轉換為 NT$2,380 的邏輯寫在這裡
         *
         * @param numberModel
         * @return
         * @throws TemplateValueFormatException
         * @throws TemplateModelException
         */
        @Override
        public String formatToPlainText(TemplateNumberModel numberModel)
                throws TemplateValueFormatException, TemplateModelException {
            Number number = TemplateFormatUtil.getNonNullNumber(numberModel);
            try {
                return ntdFormat.format(NumberUtil.toIntExact(number));
            } catch (RuntimeException e) {
                throw new UnformattableValueException(number + " - 金額轉換失敗", e);
            }
        }

        @Override
        public boolean isLocaleBound() {
            return false;
        }

        @Override
        public String getDescription() {
            return "數字轉新台幣";
        }
    }
}

2. Register to freemarker configuration class:
    Configuration cfg = new Configuration(Configuration.getVersion());
    cfg.setClassForTemplateLoading(this.getClass(), "/");
    
    // register the "ntd" format:
    Map<String, TemplateNumberFormatFactory> customNumberFormats = new HashMap<>();
    customNumberFormats.put("ntd", NTDTemplateNumberFormatFactory.INSTANCE);
    
    cfg.setCustomNumberFormats(customNumberFormats);

3. Use this format in ftl template file:
<#-- 取得現在時間 -->
<#assign aDateTime = .now>
<#-- 只保留日期 -->
<#assign aDate = aDateTime?date>
<#-- create the macro variable: -->
<#macro printDate mingguodate>
列印日期:${mingguodate}

</#macro>
<#macro footer records amount>
${"-"?right_pad(25, "-")}
總筆數:${records}
總金額:${amount}
</#macro>
<#-- call the macro: -->
<@printDate mingguodate="${aDate?string.@mingguo}" />
<#assign total = 0>
${"水果名稱"?right_pad(10)}${"單價"?right_pad(10)}${"訂購數量"?right_pad(10)}
${"-"?right_pad(25, "-")}
<#list rows as row>
<#assign total += row.price * row.quantity>
${row.name?right_pad(10)}${row.price?string.@ntd?left_pad(10)}${row.quantity?left_pad(10)}
</#list>
<#-- call the macro: -->
<@footer records="${rows?size?left_pad(10)}" amount="${total?string.@ntd?left_pad(10)}" />


Reference

No comments: