in freemarker-core/src/main/java/freemarker/core/EvalUtil.java [183:337]
static boolean compare(
TemplateModel leftValue, Expression leftExp,
int operator, String operatorString,
TemplateModel rightValue, Expression rightExp,
Expression defaultBlamed, boolean quoteOperandsInErrors,
boolean typeMismatchMeansNotEqual,
boolean leftNullReturnsFalse, boolean rightNullReturnsFalse,
Environment env) throws TemplateException {
if (leftValue == null) {
if (env != null && env.isClassicCompatible()) {
leftValue = TemplateScalarModel.EMPTY_STRING;
} else {
if (leftNullReturnsFalse) {
return false;
} else {
if (leftExp != null) {
throw InvalidReferenceException.getInstance(leftExp, env);
} else {
throw new _MiscTemplateException(defaultBlamed, env,
"The left operand of the comparison was undefined or null.");
}
}
}
}
if (rightValue == null) {
if (env != null && env.isClassicCompatible()) {
rightValue = TemplateScalarModel.EMPTY_STRING;
} else {
if (rightNullReturnsFalse) {
return false;
} else {
if (rightExp != null) {
throw InvalidReferenceException.getInstance(rightExp, env);
} else {
throw new _MiscTemplateException(defaultBlamed, env,
"The right operand of the comparison was undefined or null.");
}
}
}
}
final int cmpResult;
if (leftValue instanceof TemplateNumberModel && rightValue instanceof TemplateNumberModel) {
Number leftNum = EvalUtil.modelToNumber((TemplateNumberModel) leftValue, leftExp);
Number rightNum = EvalUtil.modelToNumber((TemplateNumberModel) rightValue, rightExp);
ArithmeticEngine ae =
env != null
? env.getArithmeticEngine()
: (leftExp != null
? leftExp.getTemplate().getArithmeticEngine()
: ArithmeticEngine.BIGDECIMAL_ENGINE);
try {
cmpResult = ae.compareNumbers(leftNum, rightNum);
} catch (RuntimeException e) {
throw new _MiscTemplateException(defaultBlamed, e, env, new Object[]
{ "Unexpected error while comparing two numbers: ", e });
}
} else if (leftValue instanceof TemplateDateModel && rightValue instanceof TemplateDateModel) {
TemplateDateModel leftDateModel = (TemplateDateModel) leftValue;
TemplateDateModel rightDateModel = (TemplateDateModel) rightValue;
int leftDateType = leftDateModel.getDateType();
int rightDateType = rightDateModel.getDateType();
if (leftDateType == TemplateDateModel.UNKNOWN || rightDateType == TemplateDateModel.UNKNOWN) {
String sideName;
Expression sideExp;
if (leftDateType == TemplateDateModel.UNKNOWN) {
sideName = "left";
sideExp = leftExp;
} else {
sideName = "right";
sideExp = rightExp;
}
throw new _MiscTemplateException(sideExp != null ? sideExp : defaultBlamed, env,
"The ", sideName, " ", VALUE_OF_THE_COMPARISON_IS_UNKNOWN_DATE_LIKE);
}
if (leftDateType != rightDateType) {
;
throw new _MiscTemplateException(defaultBlamed, env,
"Can't compare dates of different types. Left date type is ",
TemplateDateModel.TYPE_NAMES.get(leftDateType), ", right date type is ",
TemplateDateModel.TYPE_NAMES.get(rightDateType), ".");
}
Date leftDate = EvalUtil.modelToDate(leftDateModel, leftExp);
Date rightDate = EvalUtil.modelToDate(rightDateModel, rightExp);
cmpResult = leftDate.compareTo(rightDate);
} else if (leftValue instanceof TemplateScalarModel && rightValue instanceof TemplateScalarModel) {
if (operator != CMP_OP_EQUALS && operator != CMP_OP_NOT_EQUALS) {
throw new _MiscTemplateException(defaultBlamed, env,
"Can't use operator \"", cmpOpToString(operator, operatorString), "\" on string values.");
}
String leftString = EvalUtil.modelToString((TemplateScalarModel) leftValue, leftExp, env);
String rightString = EvalUtil.modelToString((TemplateScalarModel) rightValue, rightExp, env);
if (env.getConfiguration().getIncompatibleImprovements().intValue() < _VersionInts.V_2_3_33) {
cmpResult = env.getCollator().compare(leftString, rightString);
} else {
cmpResult = Normalizer.normalize(leftString, Normalizer.Form.NFKC)
.compareTo(Normalizer.normalize(rightString, Normalizer.Form.NFKC));
}
} else if (leftValue instanceof TemplateBooleanModel && rightValue instanceof TemplateBooleanModel) {
if (operator != CMP_OP_EQUALS && operator != CMP_OP_NOT_EQUALS) {
throw new _MiscTemplateException(defaultBlamed, env,
"Can't use operator \"", cmpOpToString(operator, operatorString), "\" on boolean values.");
}
boolean leftBool = ((TemplateBooleanModel) leftValue).getAsBoolean();
boolean rightBool = ((TemplateBooleanModel) rightValue).getAsBoolean();
cmpResult = (leftBool ? 1 : 0) - (rightBool ? 1 : 0);
} else if (env.isClassicCompatible()) {
String leftString = leftExp != null
? leftExp.evalAndCoerceToPlainText(env)
: EvalUtil.coerceModelToPlainText(leftValue, null, null, env);
String rightString = rightExp != null
? rightExp.evalAndCoerceToPlainText(env)
: EvalUtil.coerceModelToPlainText(rightValue, null, null, env);;
cmpResult = env.getCollator().compare(leftString, rightString);
} else {
if (typeMismatchMeansNotEqual) {
if (operator == CMP_OP_EQUALS) {
return false;
} else if (operator == CMP_OP_NOT_EQUALS) {
return true;
}
// Falls through
}
throw new _MiscTemplateException(defaultBlamed, env,
"Can't compare values of these types. ",
"Allowed comparisons are between two numbers, two strings, two dates, or two booleans.\n",
"Left hand operand ",
(quoteOperandsInErrors && leftExp != null
? new Object[] { "(", new _DelayedGetCanonicalForm(leftExp), ") value " }
: ""),
"is ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(leftValue)), ".\n",
"Right hand operand ",
(quoteOperandsInErrors && rightExp != null
? new Object[] { "(", new _DelayedGetCanonicalForm(rightExp), ") value " }
: ""),
"is ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(rightValue)),
".");
}
switch (operator) {
case CMP_OP_EQUALS: return cmpResult == 0;
case CMP_OP_NOT_EQUALS: return cmpResult != 0;
case CMP_OP_LESS_THAN: return cmpResult < 0;
case CMP_OP_GREATER_THAN: return cmpResult > 0;
case CMP_OP_LESS_THAN_EQUALS: return cmpResult <= 0;
case CMP_OP_GREATER_THAN_EQUALS: return cmpResult >= 0;
default: throw new BugException("Unsupported comparator operator code: " + operator);
}
}