blob: a669bc8ee28ca2b1517e004f853fb7706f63f76a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.core;
import java.util.Locale;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.utility.NumberUtil;
import freemarker.template.utility.StringUtil;
/**
* Shows a number in base N number system. Can only format numbers that fit into an {@code int},
* however, optionally you can specify a fallback format. This format has one required parameter,
* the numerical system base. That can be optionally followed by "|" and a fallback format.
*/
public class BaseNTemplateNumberFormatFactory extends TemplateNumberFormatFactory {
public static final BaseNTemplateNumberFormatFactory INSTANCE
= new BaseNTemplateNumberFormatFactory();
private BaseNTemplateNumberFormatFactory() {
// Defined to decrease visibility
}
@Override
public TemplateNumberFormat get(String params, Locale locale, Environment env)
throws InvalidFormatParametersException {
TemplateNumberFormat fallbackFormat;
{
int barIdx = params.indexOf('|');
if (barIdx != -1) {
String fallbackFormatStr = params.substring(barIdx + 1);
params = params.substring(0, barIdx);
try {
fallbackFormat = env.getTemplateNumberFormat(fallbackFormatStr, locale);
} catch (TemplateValueFormatException e) {
throw new InvalidFormatParametersException(
"Couldn't get the fallback number format (specified after the \"|\"), "
+ StringUtil.jQuote(fallbackFormatStr) + ". Reason: " + e.getMessage(),
e);
}
} else {
fallbackFormat = null;
}
}
int base;
try {
base = Integer.parseInt(params);
} catch (NumberFormatException e) {
if (params.length() == 0) {
throw new InvalidFormatParametersException(
"A format parameter is required to specify the numerical system base.");
}
throw new InvalidFormatParametersException(
"The format paramter must be an integer, but was (shown quoted): "
+ StringUtil.jQuote(params));
}
if (base < 2) {
throw new InvalidFormatParametersException("A base must be at least 2.");
}
return new BaseNTemplateNumberFormat(base, fallbackFormat);
}
private static class BaseNTemplateNumberFormat extends TemplateNumberFormat {
private final int base;
private final TemplateNumberFormat fallbackFormat;
private BaseNTemplateNumberFormat(int base, TemplateNumberFormat fallbackFormat) {
this.base = base;
this.fallbackFormat = fallbackFormat;
}
@Override
public String formatToPlainText(TemplateNumberModel numberModel)
throws TemplateModelException, TemplateValueFormatException {
Number n = TemplateFormatUtil.getNonNullNumber(numberModel);
try {
return Integer.toString(NumberUtil.toIntExact(n), base);
} catch (ArithmeticException e) {
if (fallbackFormat == null) {
throw new UnformattableValueException(
n + " doesn't fit into an int, and there was no fallback format "
+ "specified.");
} else {
return fallbackFormat.formatToPlainText(numberModel);
}
}
}
@Override
public boolean isLocaleBound() {
return false;
}
@Override
public String getDescription() {
return "base " + base;
}
}
}