2021SC@SDUSC
目录
AddConcatExpression.java
源码分析
/*
* 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.HashSet;
import java.util.Set;
import freemarker.template.SimpleNumber;
import freemarker.template.SimpleScalar;
import freemarker.template.SimpleSequence;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelIterator;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
import freemarker.template._TemplateAPI;
//运算符的运算符。注意,这是处理过的*与其他4个算术运算符分开,* since +重载意味着字符串连接。
final class AddConcatExpression extends Expression {
private final Expression left;
private final Expression right;
AddConcatExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
TemplateModel _eval(Environment env) throws TemplateException {
return _eval(env, this, left, left.eval(env), right, right.eval(env));
}
/**
* @param leftExp
* Used for error messages only; can be {@code null}
* @param rightExp
* Used for error messages only; can be {@code null}
*/
static TemplateModel _eval(Environment env,
TemplateObject parent,
Expression leftExp, TemplateModel leftModel,
Expression rightExp, TemplateModel rightModel)
throws TemplateModelException, TemplateException, NonStringException {
if (leftModel instanceof TemplateNumberModel && rightModel instanceof TemplateNumberModel) {
Number first = EvalUtil.modelToNumber((TemplateNumberModel) leftModel, leftExp);
Number second = EvalUtil.modelToNumber((TemplateNumberModel) rightModel, rightExp);
return _evalOnNumbers(env, parent, first, second);
} else if (leftModel instanceof TemplateSequenceModel && rightModel instanceof TemplateSequenceModel) {
return new ConcatenatedSequence((TemplateSequenceModel) leftModel, (TemplateSequenceModel) rightModel);
} else {
boolean hashConcatPossible
= leftModel instanceof TemplateHashModel && rightModel instanceof TemplateHashModel;
try {
// 弦加法。如果哈希加法是可能的,则代替抛出异常返回null,然后做哈希加法。
Object leftOMOrStr = EvalUtil.coerceModelToStringOrMarkup(
leftModel, leftExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
env);
if (leftOMOrStr == null) {
return _eval_concatenateHashes(leftModel, rightModel);
}
// 与上面的null返回相同的技巧。.
Object rightOMOrStr = EvalUtil.coerceModelToStringOrMarkup(
rightModel, rightExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
env);
if (rightOMOrStr == null) {
return _eval_concatenateHashes(leftModel, rightModel);
}
if (leftOMOrStr instanceof String) {
if (rightOMOrStr instanceof String) {
return new SimpleScalar(((String) leftOMOrStr).concat((String) rightOMOrStr));
} else { // rightOMOrStr instanceof TemplateMarkupOutputModel
TemplateMarkupOutputModel<?> rightMO = (TemplateMarkupOutputModel<?>) rightOMOrStr;
return EvalUtil.concatMarkupOutputs(parent,
rightMO.getOutputFormat().fromPlainTextByEscaping((String) leftOMOrStr),
rightMO);
}
} else { // leftOMOrStr instanceof TemplateMarkupOutputModel
TemplateMarkupOutputModel<?> leftMO = (TemplateMarkupOutputModel<?>) leftOMOrStr;
if (rightOMOrStr instanceof String) { // markup output
return EvalUtil.concatMarkupOutputs(parent,
leftMO,
leftMO.getOutputFormat().fromPlainTextByEscaping((String) rightOMOrStr));
} else { // rightOMOrStr instanceof TemplateMarkupOutputModel
return EvalUtil.concatMarkupOutputs(parent,
leftMO,
(TemplateMarkupOutputModel<?>) rightOMOrStr);
}
}
} catch (NonStringOrTemplateOutputException e) {
// 移除此catch;它是针对BC的,在重新处理哈希加法之后,所以它不依赖于这个。
if (hashConcatPossible) {
return _eval_concatenateHashes(leftModel, rightModel);
} else {
throw e;
}
}
}
}
//用户代码可能会抛出这个(非常不可能)
private static TemplateModel _eval_concatenateHashes(TemplateModel leftModel, TemplateModel rightModel)
throws TemplateModelException {
if (leftModel instanceof TemplateHashModelEx && rightModel instanceof TemplateHashModelEx) {
TemplateHashModelEx leftModelEx = (TemplateHashModelEx) leftModel;
TemplateHashModelEx rightModelEx = (TemplateHashModelEx) rightModel;
if (leftModelEx.size() == 0) {
return rightModelEx;
} else if (rightModelEx.size() == 0) {
return leftModelEx;
} else {
return new ConcatenatedHashEx(leftModelEx, rightModelEx);
}
} else {
return new ConcatenatedHash((TemplateHashModel) leftModel,
(TemplateHashModel) rightModel);
}
}
static TemplateModel _evalOnNumbers(Environment env, TemplateObject parent, Number first, Number second)
throws TemplateException {
ArithmeticEngine ae = EvalUtil.getArithmeticEngine(env, parent);
return new SimpleNumber(ae.add(first, second));
}
@Override
boolean isLiteral() {
return constantValue != null || (left.isLiteral() && right.isLiteral());
}
@Override
protected Expression deepCloneWithIdentifierReplaced_inner(
String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
return new AddConcatExpression(
left.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState),
right.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState));
}
@Override
public String getCanonicalForm() {
return left.getCanonicalForm() + " + " + right.getCanonicalForm();
}
@Override
String getNodeTypeSymbol() {
return "+";
}
@Override
int getParameterCount() {
return 2;
}
@Override
Object getParameterValue(int idx) {
return idx == 0 ? left : right;
}
@Override
ParameterRole getParameterRole(int idx) {
return ParameterRole.forBinaryOperatorOperand(idx);
}
private static final class ConcatenatedSequence
implements
TemplateSequenceModel {
private final TemplateSequenceModel left;
private final TemplateSequenceModel right;
ConcatenatedSequence(TemplateSequenceModel left, TemplateSequenceModel right) {
this.left = left;
this.right = right;
}
@Override
public int size()
throws TemplateModelException {
return left.size() + right.size();
}
@Override
public TemplateModel get(int i)
throws TemplateModelException {
int ls = left.size();
return i < ls ? left.get(i) : right.get(i - ls);
}
}
private static class ConcatenatedHash
implements TemplateHashModel {
protected final TemplateHashModel left;
protected final TemplateHashModel right;
ConcatenatedHash(TemplateHashModel left, TemplateHashModel right) {
this.left = left;
this.right = right;
}
@Override
public TemplateModel get(String key)
throws TemplateModelException {
TemplateModel model = right.get(key);
return (model != null) ? model : left.get(key);
}
@Override
public boolean isEmpty()
throws TemplateModelException {
return left.isEmpty() && right.isEmpty();
}
}
private static final class ConcatenatedHashEx
extends ConcatenatedHash
implements TemplateHashModelEx {
private CollectionAndSequence keys;
private CollectionAndSequence values;
ConcatenatedHashEx(TemplateHashModelEx left, TemplateHashModelEx right) {
super(left, right);
}
@Override
public int size() throws TemplateModelException {
initKeys();
return keys.size();
}
@Override
public TemplateCollectionModel keys()
throws TemplateModelException {
initKeys();
return keys;
}
@Override
public TemplateCollectionModel values()
throws TemplateModelException {
initValues();
return values;
}
private void initKeys()
throws TemplateModelException {
if (keys == null) {
HashSet keySet = new HashSet();
SimpleSequence keySeq = new SimpleSequence(32, _TemplateAPI.SAFE_OBJECT_WRAPPER);
addKeys(keySet, keySeq, (TemplateHashModelEx) this.left);
addKeys(keySet, keySeq, (TemplateHashModelEx) this.right);
keys = new CollectionAndSequence(keySeq);
}
}
private static void addKeys(Set keySet, SimpleSequence keySeq, TemplateHashModelEx hash)
throws TemplateModelException {
TemplateModelIterator it = hash.keys().iterator();
while (it.hasNext()) {
TemplateScalarModel tsm = (TemplateScalarModel) it.next();
if (keySet.add(tsm.getAsString())) {
//第一个出现的键决定索引;
keySeq.add(tsm);
}
}
}
//这与java.util.LinkedHashSet的行为一致
private void initValues()
throws TemplateModelException {
if (values == null) {
SimpleSequence seq = new SimpleSequence(size(), _TemplateAPI.SAFE_OBJECT_WRAPPER);
//注意:如果需要,size()会调用initKeys
int ln = keys.size();
for (int i = 0; i < ln; i++) {
seq.add(get(((TemplateScalarModel) keys.get(i)).getAsString()));
}
values = new CollectionAndSequence(seq);
}
}
}
}
BuiltInsForMultipleTypes.java
源码分析
/*
* 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.Date;
import java.util.List;
import freemarker.ext.beans.BeanModel;
import freemarker.ext.beans.OverloadedMethodsModel;
import freemarker.ext.beans.SimpleMethodModel;
import freemarker.ext.beans._BeansAPI;
import freemarker.template.SimpleDate;
import freemarker.template.SimpleNumber;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateCollectionModelEx;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateMethodModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelIterator;
import freemarker.template.TemplateModelWithAPISupport;
import freemarker.template.TemplateNodeModel;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
import freemarker.template.TemplateTransformModel;
import freemarker.template._TemplateAPI;
import freemarker.template.utility.NumberUtil;
//不属于任何其他类别的内置组件的支架
class BuiltInsForMultipleTypes {
static class cBI extends AbstractCBI implements ICIChainMember {
static class BIBeforeICI2d3d21 extends AbstractCBI {
@Override
protected TemplateModel formatNumber(Environment env, TemplateModel model) throws TemplateModelException {
Number num = EvalUtil.modelToNumber((TemplateNumberModel) model, target);
if (num instanceof Integer || num instanceof Long) {
// 加速这些相当常见的情况
return new SimpleScalar(num.toString());
} else {
return new SimpleScalar(env.getCNumberFormat().format(num));
}
}
}
private final BIBeforeICI2d3d21 prevICIObj = new BIBeforeICI2d3d21();
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel model = target.eval(env);
if (model instanceof TemplateNumberModel) {
return formatNumber(env, model);
} else if (model instanceof TemplateBooleanModel) {
return new SimpleScalar(((TemplateBooleanModel) model).getAsBoolean()
? MiscUtil.C_TRUE : MiscUtil.C_FALSE);
} else {
throw new UnexpectedTypeException(
target, model,
"number or boolean", new Class[] { TemplateNumberModel.class, TemplateBooleanModel.class },
env);
}
}
@Override
protected TemplateModel formatNumber(Environment env, TemplateModel model) throws TemplateModelException {
Number num = EvalUtil.modelToNumber((TemplateNumberModel) model, target);
if (num instanceof Integer || num instanceof Long) {
// 加速这些相当常见的情况
return new SimpleScalar(num.toString());
} else if (num instanceof Double) {
double n = num.doubleValue();
if (n == Double.POSITIVE_INFINITY) {
return new SimpleScalar("INF");
}
if (n == Double.NEGATIVE_INFINITY) {
return new SimpleScalar("-INF");
}
if (Double.isNaN(n)) {
return new SimpleScalar("NaN");
}
// 故意失败
} else if (num instanceof Float) {
float n = num.floatValue();
if (n == Float.POSITIVE_INFINITY) {
return new SimpleScalar("INF");
}
if (n == Float.NEGATIVE_INFINITY) {
return new SimpleScalar("-INF");
}
if (Float.isNaN(n)) {
return new SimpleScalar("NaN");
}
// 故意失败
}
return new SimpleScalar(env.getCNumberFormat().format(num));
}
@Override
public int getMinimumICIVersion() {
return _TemplateAPI.VERSION_INT_2_3_21;
}
@Override
public Object getPreviousICIChainMember() {
return prevICIObj;
}
}
static class dateBI extends BuiltIn {
private class DateParser
implements
TemplateDateModel,
TemplateMethodModel,
TemplateHashModel {
private final String text;
private final Environment env;
private final TemplateDateFormat defaultFormat;
private TemplateDateModel cachedValue;
DateParser(String text, Environment env)
throws TemplateException {
this.text = text;
this.env = env;
this.defaultFormat = env.getTemplateDateFormat(dateType, Date.class, target, false);
}
@Override
public Object exec(List args) throws TemplateModelException {
checkMethodArgCount(args, 0, 1);
return args.size() == 0 ? getAsDateModel() : get((String) args.get(0));
}
@Override
public TemplateModel get(String pattern) throws TemplateModelException {
TemplateDateFormat format;
try {
format = env.getTemplateDateFormat(pattern, dateType, Date.class, target, dateBI.this, true);
} catch (TemplateException e) {
// 在这里' e '应该总是一个TemplateModelException
}
return toTemplateDateModel(parse(format));
}
private TemplateDateModel toTemplateDateModel(Object date) throws _TemplateModelException {
if (date instanceof Date) {
return new SimpleDate((Date) date, dateType);
} else {
TemplateDateModel tm = (TemplateDateModel) date;
if (tm.getDateType() != dateType) {
throw new _TemplateModelException("The result of the parsing was of the wrong date type.");
}
return tm;
}
}
private TemplateDateModel getAsDateModel() throws TemplateModelException {
if (cachedValue == null) {
cachedValue = toTemplateDateModel(parse(defaultFormat));
}
return cachedValue;
}
@Override
public Date getAsDate() throws TemplateModelException {
return getAsDateModel().getAsDate();
}
@Override
public int getDateType() {
return dateType;
}
@Override
public boolean isEmpty() {
return false;
}
private Object parse(TemplateDateFormat df)
throws TemplateModelException {
try {
return df.parse(text, dateType);
} catch (TemplateValueFormatException e) {
throw new _TemplateModelException(e,
"The string doesn't match the expected date/time/date-time format. "
+ "The string to parse was: ", new _DelayedJQuote(text), ". ",
"The expected format was: ", new _DelayedJQuote(df.getDescription()), ".",
e.getMessage() != null ? "\nThe nested reason given follows:\n" : "",
e.getMessage() != null ? e.getMessage() : "");
}
}
}
private final int dateType;
dateBI(int dateType) {
this.dateType = dateType;
}
@Override
TemplateModel _eval(Environment env)
throws TemplateException {
TemplateModel model = target.eval(env);
if (model instanceof TemplateDateModel) {
TemplateDateModel dmodel = (TemplateDateModel) model;
int dtype = dmodel.getDateType();
// 任何日期模型都可以强制转换为自己的类型
if (dateType == dtype) {
return model;
}
// unknown and datetime can be coerced into any date type
if (dtype == TemplateDateModel.UNKNOWN || dtype == TemplateDateModel.DATETIME) {
return new SimpleDate(dmodel.getAsDate(), dateType);
}
throw new _MiscTemplateException(this,
"Cannot convert ", TemplateDateModel.TYPE_NAMES.get(dtype),
" to ", TemplateDateModel.TYPE_NAMES.get(dateType));
}
//否则,解释为字符串并尝试
//将其解析为日期。
String s = target.evalAndCoerceToPlainText(env);
return new DateParser(s, env);
}
}
static class apiBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
if (!env.isAPIBuiltinEnabled()) {
throw new _MiscTemplateException(this,
"Can't use ?api, because the \"", Configurable.API_BUILTIN_ENABLED_KEY,
"\" configuration setting is false. Think twice before you set it to true though. Especially, "
+ "it shouldn't abused for modifying Map-s and Collection-s.");
}
final TemplateModel tm = target.eval(env);
if (!(tm instanceof TemplateModelWithAPISupport)) {
target.assertNonNull(tm, env);
throw new APINotSupportedTemplateException(env, target, tm);
}
return ((TemplateModelWithAPISupport) tm).getAPI();
}
}
static class has_apiBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
final TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return tm instanceof TemplateModelWithAPISupport ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_booleanBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateBooleanModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_collectionBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateCollectionModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_collection_exBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateCollectionModelEx) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_dateLikeBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateDateModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_dateOfTypeBI extends BuiltIn {
private final int dateType;
is_dateOfTypeBI(int dateType) {
this.dateType = dateType;
}
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateDateModel) && ((TemplateDateModel) tm).getDateType() == dateType
? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_directiveBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
// 错误:它还必须检查Macro.isFunction()
return (tm instanceof TemplateTransformModel || tm instanceof Macro || tm instanceof TemplateDirectiveModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_enumerableBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateSequenceModel || tm instanceof TemplateCollectionModel)
&& (_TemplateAPI.getTemplateLanguageVersionAsInt(this) < _TemplateAPI.VERSION_INT_2_3_21
//这些实现了TemplateSequenceModel,但它们不能被#list:
|| !(tm instanceof SimpleMethodModel || tm instanceof OverloadedMethodsModel))
? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_hash_exBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateHashModelEx) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_hashBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateHashModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_indexableBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateSequenceModel) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_macroBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
// WRONG: it also had to check Macro.isFunction()
return (tm instanceof Macro) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_markup_outputBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateMarkupOutputModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_methodBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateMethodModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_nodeBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateNodeModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_numberBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateNumberModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_sequenceBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateSequenceModel
&& (
!(tm instanceof OverloadedMethodsModel || tm instanceof SimpleMethodModel)
|| !env.isIcI2324OrLater())
)
? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_stringBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateScalarModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class is_transformBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
target.assertNonNull(tm, env);
return (tm instanceof TemplateTransformModel) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
}
}
static class namespaceBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel tm = target.eval(env);
if (!(tm instanceof Macro)) {
throw new UnexpectedTypeException(
target, tm,
"macro or function", new Class[] { Macro.class },
env);
} else {
return env.getMacroNamespace((Macro) tm);
}
}
}
static class sizeBI extends BuiltIn {
@Override
protected void setTarget(Expression target) {
super.setTarget(target);
target.enableLazilyGeneratedResult();
}
private int countingLimit;
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel model = target.eval(env);
final int size;
if (countingLimit == 1 && model instanceof TemplateCollectionModelEx) {
size = ((TemplateCollectionModelEx) model).isEmpty() ? 0 : 1;
} else if (model instanceof TemplateSequenceModel) {
size = ((TemplateSequenceModel) model).size();
} else if (model instanceof TemplateCollectionModelEx) {
size = ((TemplateCollectionModelEx) model).size();
} else if (model instanceof TemplateHashModelEx) {
size = ((TemplateHashModelEx) model).size();
} else if (model instanceof LazilyGeneratedCollectionModel
&& ((LazilyGeneratedCollectionModel) model).isSequence()) {
// 它是?filter(predicate)的结果。那些“正式”返回一个序列。返回一个TemplateCollectionModel(确切地说是一个LazilyGeneratedCollectionModel)是一个(几乎)透明的模型
TemplateModelIterator iterator = ((LazilyGeneratedCollectionModel) model).iterator();
int counter = 0;
countElements: while (iterator.hasNext()) {
counter++;
if (counter == countingLimit) {
break countElements;
}
iterator.next();
}
size = counter;
} else {
throw new UnexpectedTypeException(
target, model,
"extended-hash or sequence or extended collection",
new Class[] {
TemplateHashModelEx.class,
TemplateSequenceModel.class,
TemplateCollectionModelEx.class
},
env);
}
return new SimpleNumber(size);
}
/**
* Enables an optimization trick when the result of the built-in will be compared with a number literal.
* For example, in the case of {@code things?size != 0} we only need to check of the target is non-empty, which
* is often more efficient than telling exactly how many elements it has.
*/
void setCountingLimit(int cmpOperator, NumberLiteral rightOperand) {
int cmpInt;
try {
cmpInt = NumberUtil.toIntExact(rightOperand.getAsNumber());
} catch (ArithmeticException e) {
//因为我们不知道运行时的算术引擎是什么,所以我们不会对非整数冒险。
return;
}
switch (cmpOperator) {
case EvalUtil.CMP_OP_EQUALS: countingLimit = cmpInt + 1; break;
case EvalUtil.CMP_OP_NOT_EQUALS: countingLimit = cmpInt + 1; break;
case EvalUtil.CMP_OP_LESS_THAN: countingLimit = cmpInt; break;
case EvalUtil.CMP_OP_GREATER_THAN: countingLimit = cmpInt + 1; break;
case EvalUtil.CMP_OP_LESS_THAN_EQUALS: countingLimit = cmpInt + 1; break;
case EvalUtil.CMP_OP_GREATER_THAN_EQUALS: countingLimit = cmpInt; break;
default: throw new BugException("Unsupported comparator operator code: " + cmpOperator);
}
}
}
static class stringBI extends BuiltIn {
private class BooleanFormatter
implements
TemplateScalarModel,
TemplateMethodModel {
private final TemplateBooleanModel bool;
private final Environment env;
BooleanFormatter(TemplateBooleanModel bool, Environment env) {
this.bool = bool;
this.env = env;
}
@Override
public Object exec(List args) throws TemplateModelException {
checkMethodArgCount(args, 2);
return new SimpleScalar((String) args.get(bool.getAsBoolean() ? 0 : 1));
}
@Override
public String getAsString() throws TemplateModelException {
// Boolean should have come first... but that change would be non-BC.
if (bool instanceof TemplateScalarModel) {
return ((TemplateScalarModel) bool).getAsString();
} else {
try {
return env.formatBoolean(bool.getAsBoolean(), true);
} catch (TemplateException e) {
throw new TemplateModelException(e);
}
}
}
}
private class DateFormatter
implements
TemplateScalarModel,
TemplateHashModel,
TemplateMethodModel {
private final TemplateDateModel dateModel;
private final Environment env;
private final TemplateDateFormat defaultFormat;
private String cachedValue;
DateFormatter(TemplateDateModel dateModel, Environment env)
throws TemplateException {
this.dateModel = dateModel;
this.env = env;
final int dateType = dateModel.getDateType();
this.defaultFormat = dateType == TemplateDateModel.UNKNOWN
? null // Lazy unknown type error in getAsString()
: env.getTemplateDateFormat(
dateType, EvalUtil.modelToDate(dateModel, target).getClass(), target, true);
}
@Override
public Object exec(List args) throws TemplateModelException {
checkMethodArgCount(args, 1);
return formatWith((String) args.get(0));
}
@Override
public TemplateModel get(String key)
throws TemplateModelException {
return formatWith(key);
}
private TemplateModel formatWith(String key)
throws TemplateModelException {
try {
return new SimpleScalar(env.formatDateToPlainText(dateModel, key, target, stringBI.this, true));
} catch (TemplateException e) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to format value", e);
}
}
@Override
public String getAsString()
throws TemplateModelException {
if (cachedValue == null) {
if (defaultFormat == null) {
if (dateModel.getDateType() == TemplateDateModel.UNKNOWN) {
throw _MessageUtil.newCantFormatUnknownTypeDateException(target, null);
} else {
throw new BugException();
}
}
try {
cachedValue = EvalUtil.assertFormatResultNotNull(defaultFormat.formatToPlainText(dateModel));
} catch (TemplateValueFormatException e) {
try {
throw _MessageUtil.newCantFormatDateException(defaultFormat, target, e, true);
} catch (TemplateException e2) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to format date/time/datetime", e2);
}
}
}
return cachedValue;
}
@Override
public boolean isEmpty() {
return false;
}
}
private class NumberFormatter
implements
TemplateScalarModel,
TemplateHashModel,
TemplateMethodModel {
private final TemplateNumberModel numberModel;
private final Number number;
private final Environment env;
private final TemplateNumberFormat defaultFormat;
private String cachedValue;
NumberFormatter(TemplateNumberModel numberModel, Environment env) throws TemplateException {
this.env = env;
// As we format lazily, we need a snapshot of the format inputs:
this.numberModel = numberModel;
number = EvalUtil.modelToNumber(numberModel, target); // for BackwardCompatibleTemplateNumberFormat-s
try {
defaultFormat = env.getTemplateNumberFormat(stringBI.this, true);
} catch (TemplateException e) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to get default number format", e);
}
}
@Override
public Object exec(List args) throws TemplateModelException {
checkMethodArgCount(args, 1);
return get((String) args.get(0));
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
TemplateNumberFormat format;
try {
format = env.getTemplateNumberFormat(key, stringBI.this, true);
} catch (TemplateException e) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to get number format", e);
}
String result;
try {
if (format instanceof BackwardCompatibleTemplateNumberFormat) {
result = env.formatNumberToPlainText(number, (BackwardCompatibleTemplateNumberFormat) format, target);
} else {
result = env.formatNumberToPlainText(numberModel, format, target, true);
}
} catch (TemplateException e) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to format number", e);
}
return new SimpleScalar(result);
}
@Override
public String getAsString() throws TemplateModelException {
if (cachedValue == null) {
try {
if (defaultFormat instanceof BackwardCompatibleTemplateNumberFormat) {
cachedValue = env.formatNumberToPlainText(
number, (BackwardCompatibleTemplateNumberFormat) defaultFormat, target);
} else {
cachedValue = env.formatNumberToPlainText(numberModel, defaultFormat, target, true);
}
} catch (TemplateException e) {
// `e` should always be a TemplateModelException here, but to be sure:
throw _CoreAPI.ensureIsTemplateModelException("Failed to format number", e);
}
}
return cachedValue;
}
@Override
public boolean isEmpty() {
return false;
}
}
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel model = target.eval(env);
if (model instanceof TemplateNumberModel) {
return new NumberFormatter((TemplateNumberModel) model, env);
} else if (model instanceof TemplateDateModel) {
TemplateDateModel dm = (TemplateDateModel) model;
return new DateFormatter(dm, env);
} else if (model instanceof SimpleScalar) {
return model;
} else if (model instanceof TemplateBooleanModel) {
return new BooleanFormatter((TemplateBooleanModel) model, env);
} else if (model instanceof TemplateScalarModel) {
return new SimpleScalar(((TemplateScalarModel) model).getAsString());
} else if (env.isClassicCompatible() && model instanceof BeanModel) {
return new SimpleScalar(_BeansAPI.getAsClassicCompatibleString((BeanModel) model));
} else {
throw new UnexpectedTypeException(
target, model,
"number, date, boolean or string",
new Class[] {
TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class,
TemplateScalarModel.class
},
env);
}
}
}
// Can't be instantiated
private BuiltInsForMultipleTypes() { }
static abstract class AbstractCBI extends BuiltIn {
@Override
TemplateModel _eval(Environment env) throws TemplateException {
TemplateModel model = target.eval(env);
if (model instanceof TemplateNumberModel) {
return formatNumber(env, model);
} else if (model instanceof TemplateBooleanModel) {
return new SimpleScalar(((TemplateBooleanModel) model).getAsBoolean()
? MiscUtil.C_TRUE : MiscUtil.C_FALSE);
} else {
throw new UnexpectedTypeException(
target, model,
"number or boolean", new Class[] { TemplateNumberModel.class, TemplateBooleanModel.class },
env);
}
}
protected abstract TemplateModel formatNumber(Environment env, TemplateModel model) throws TemplateModelException;
}
}