in freemarker-core/src/main/java/freemarker/core/Macro.java [255:400]
void checkParamsSetAndApplyDefaults(Environment env) throws TemplateException {
boolean resolvedADefaultValue, hasUnresolvedDefaultValue;
Expression firstUnresolvedDefaultValueExpression;
InvalidReferenceException firstInvalidReferenceExceptionForDefaultValue;
final TemplateModel[] argsSpecVarDraft;
if (Macro.this.requireArgsSpecialVariable) {
argsSpecVarDraft = new TemplateModel[paramNames.length];
} else {
argsSpecVarDraft = null;
}
do { // Retried if there are unresolved defaults left
firstUnresolvedDefaultValueExpression = null;
firstInvalidReferenceExceptionForDefaultValue = null;
resolvedADefaultValue = hasUnresolvedDefaultValue = false;
for (int paramIndex = 0; paramIndex < paramNames.length; ++paramIndex) {
final String argName = paramNames[paramIndex];
final TemplateModel argValue = localVars.get(argName);
if (argValue == null) {
Expression defaultValueExp = paramNamesWithDefault.get(argName);
if (defaultValueExp != null) {
try {
TemplateModel defaultValue = defaultValueExp.eval(env);
if (defaultValue == null) {
if (!hasUnresolvedDefaultValue) {
firstUnresolvedDefaultValueExpression = defaultValueExp;
hasUnresolvedDefaultValue = true;
}
} else {
localVars.put(argName, defaultValue);
resolvedADefaultValue = true;
if (argsSpecVarDraft != null) {
argsSpecVarDraft[paramIndex] = defaultValue;
}
}
} catch (InvalidReferenceException e) {
if (!hasUnresolvedDefaultValue) {
hasUnresolvedDefaultValue = true;
firstInvalidReferenceExceptionForDefaultValue = e;
}
}
} else if (!env.isClassicCompatible()) {
boolean argWasSpecified = localVars.containsKey(argName);
throw new _MiscTemplateException(env,
new _ErrorDescriptionBuilder(
"When calling ", (isFunction() ? "function" : "macro"), " ",
new _DelayedJQuote(name),
", required parameter ", new _DelayedJQuote(argName),
" (parameter #", Integer.valueOf(paramIndex + 1), ") was ",
(argWasSpecified
? "specified, but had null/missing value."
: "not specified.")
).tip(argWasSpecified
? new Object[] {
"If the parameter value expression on the caller side is known to "
+ "be legally null/missing, you may want to specify a default "
+ "value for it with the \"!\" operator, like "
+ "paramValue!defaultValue." }
: new Object[] {
"If the omission was deliberate, you may consider making the "
+ "parameter optional in the macro by specifying a default value "
+ "for it, like ", "<#macro macroName paramName=defaultExpr>", ")" }
));
}
} else if (argsSpecVarDraft != null) {
// Minor performance problem here: If there are multiple iterations due to default value
// dependencies, this will set many parameters for multiple times.
argsSpecVarDraft[paramIndex] = argValue;
}
}
} while (hasUnresolvedDefaultValue && resolvedADefaultValue);
if (hasUnresolvedDefaultValue) {
if (firstInvalidReferenceExceptionForDefaultValue != null) {
throw firstInvalidReferenceExceptionForDefaultValue;
} else if (!env.isClassicCompatible()) {
throw InvalidReferenceException.getInstance(firstUnresolvedDefaultValueExpression, env);
}
}
if (argsSpecVarDraft != null) {
final String catchAllParamName = getMacro().catchAllParamName;
final TemplateModel catchAllArgValue = catchAllParamName != null
? localVars.get(catchAllParamName) : null;
if (getMacro().isFunction()) {
int lengthWithCatchAlls = argsSpecVarDraft.length;
if (catchAllArgValue != null) {
lengthWithCatchAlls += ((TemplateSequenceModel) catchAllArgValue).size();
}
SimpleSequence argsSpecVarValue = new SimpleSequence(
lengthWithCatchAlls, _ObjectWrappers.SAFE_OBJECT_WRAPPER);
for (int paramIndex = 0; paramIndex < argsSpecVarDraft.length; paramIndex++) {
argsSpecVarValue.add(argsSpecVarDraft[paramIndex]);
}
if (catchAllParamName != null) {
TemplateSequenceModel catchAllSeq = (TemplateSequenceModel) catchAllArgValue;
int catchAllSize = catchAllSeq.size();
for (int j = 0; j < catchAllSize; j++) {
argsSpecVarValue.add(catchAllSeq.get(j));
}
}
assert argsSpecVarValue.size() == lengthWithCatchAlls;
this.argsSpecialVariableValue = argsSpecVarValue;
} else { // #macro
int lengthWithCatchAlls = argsSpecVarDraft.length;
TemplateHashModelEx2 catchAllHash;
if (catchAllParamName != null) {
if (catchAllArgValue instanceof TemplateSequenceModel) {
if (((TemplateSequenceModel) catchAllArgValue).size() != 0) {
throw new _MiscTemplateException("The macro can only by called with named arguments, " +
"because it uses both .", BuiltinVariable.ARGS, " and a non-empty catch-all " +
"parameter.");
}
catchAllHash = Constants.EMPTY_HASH_EX2;
} else {
catchAllHash = (TemplateHashModelEx2) catchAllArgValue;
}
lengthWithCatchAlls += catchAllHash.size();
} else {
catchAllHash = null;
}
SimpleHash argsSpecVarValue = new SimpleHash(
new LinkedHashMap<String, Object>(lengthWithCatchAlls * 4 / 3, 1.0f),
_ObjectWrappers.SAFE_OBJECT_WRAPPER, 0);
for (int paramIndex = 0; paramIndex < argsSpecVarDraft.length; paramIndex++) {
argsSpecVarValue.put(paramNames[paramIndex], argsSpecVarDraft[paramIndex]);
}
if (catchAllArgValue != null) {
for (TemplateHashModelEx2.KeyValuePairIterator iter = catchAllHash.keyValuePairIterator();
iter.hasNext(); ) {
TemplateHashModelEx2.KeyValuePair kvp = iter.next();
argsSpecVarValue.put(
((TemplateScalarModel) kvp.getKey()).getAsString(),
kvp.getValue());
}
}
assert argsSpecVarValue.size() == lengthWithCatchAlls;
this.argsSpecialVariableValue = argsSpecVarValue;
}
} // if (argsSpecVarDraft != null)
}