blob: 913eb11fbecb7525b366372fe05e03e7cdc0a8a5 [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 org.apache.freemarker.core;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Set;
import org.apache.freemarker.core.ASTDirMacroOrFunction.Context;
import org.apache.freemarker.core.model.TemplateDateModel;
import org.apache.freemarker.core.model.TemplateHashModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateStringModel;
import org.apache.freemarker.core.model.impl.SimpleDate;
import org.apache.freemarker.core.model.impl.SimpleString;
import org.apache.freemarker.core.util._SortedArraySet;
import org.apache.freemarker.core.util._StringUtils;
/**
* AST expression node: {@code .name}
*/
final class ASTExpBuiltInVariable extends ASTExpression {
static final String MAIN_TEMPLATE_NAME = "mainTemplateName";
static final String CURRENT_TEMPLATE_NAME = "currentTemplateName";
static final String NAMESPACE = "namespace";
static final String MAIN = "main";
static final String GLOBALS = "globals";
static final String LOCALS = "locals";
static final String DATA_MODEL = "dataModel";
static final String LANG = "lang";
static final String LOCALE = MutableProcessingConfiguration.LOCALE_KEY;
static final String LOCALE_OBJECT = MutableProcessingConfiguration.LOCALE_KEY + "Object";
static final String TIME_ZONE = MutableProcessingConfiguration.TIME_ZONE_KEY;
static final String NODE = "node";
static final String PASS = "pass";
static final String VARS = "vars";
static final String VERSION = "version";
static final String INCOMPATIBLE_IMPROVEMENTS = Configuration.Builder.INCOMPATIBLE_IMPROVEMENTS_KEY;
static final String ERROR = "error";
static final String OUTPUT_ENCODING = MutableProcessingConfiguration.OUTPUT_ENCODING_KEY;
static final String OUTPUT_FORMAT = MutableParsingAndProcessingConfiguration.OUTPUT_FORMAT_KEY;
static final String AUTO_ESC = "autoEsc";
static final String URL_ESCAPING_CHARSET = MutableProcessingConfiguration.URL_ESCAPING_CHARSET_KEY;
static final String NOW = "now";
static final String GET_OPTIONAL_TEMPLATE = "getOptionalTemplate";
static final String CALLER_TEMPLATE_NAME = "callerTemplateName";
static final Set<String> BUILT_IN_VARIABLE_NAMES = new _SortedArraySet<>(
// IMPORTANT! Keep this sorted alphabetically!
AUTO_ESC,
CALLER_TEMPLATE_NAME,
CURRENT_TEMPLATE_NAME,
DATA_MODEL,
ERROR,
GET_OPTIONAL_TEMPLATE,
GLOBALS,
INCOMPATIBLE_IMPROVEMENTS,
LANG,
LOCALE,
LOCALE_OBJECT,
LOCALS,
MAIN,
MAIN_TEMPLATE_NAME,
NAMESPACE,
NODE,
NOW,
OUTPUT_ENCODING,
OUTPUT_FORMAT,
PASS,
TIME_ZONE,
URL_ESCAPING_CHARSET,
VARS,
VERSION
);
private final String name;
private final TemplateModel parseTimeValue;
ASTExpBuiltInVariable(Token nameTk, FMParserTokenManager tokenManager, TemplateModel parseTimeValue)
throws ParseException {
String name = nameTk.image;
this.parseTimeValue = parseTimeValue;
if (!BUILT_IN_VARIABLE_NAMES.contains(name)) {
StringBuilder sb = new StringBuilder();
sb.append("Unknown special variable name: ");
sb.append(_StringUtils.jQuote(name)).append(".");
String correctedName;
if (name.indexOf('_') != -1) {
sb.append(MessageUtils.FM3_SNAKE_CASE);
correctedName = _StringUtils.snakeCaseToCamelCase(name);
if (!BUILT_IN_VARIABLE_NAMES.contains(correctedName)) {
correctedName = null;
}
} else if (name.equals("auto_escape") || name.equals("auto_escaping") || name.equals("autoEsc")
|| name.equals("autoEscape") || name.equals("autoEscaping")) {
correctedName = "autoEsc";
} else {
correctedName = null;
}
if (correctedName != null) {
sb.append("\nThe correct name is: ").append(correctedName);
} else {
sb.append("\nThe supported special variable names are: ");
boolean first = true;
for (final String supportedName : BUILT_IN_VARIABLE_NAMES) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(supportedName);
}
}
throw new ParseException(sb.toString(), null, nameTk);
}
this.name = name.intern();
}
@Override
TemplateModel _eval(Environment env) throws TemplateException {
if (parseTimeValue != null) {
return parseTimeValue;
}
if (name == NAMESPACE) {
return env.getCurrentNamespace();
}
if (name == MAIN) {
return env.getMainNamespace();
}
if (name == GLOBALS) {
return env.getGloballyVisibleVariables();
}
if (name == LOCALS) {
ASTDirMacroOrFunction.Context ctx = env.getCurrentMacroContext();
return ctx == null ? null : ctx.getLocals();
}
if (name == DATA_MODEL) {
return env.getDataModelWithSharedVariableFallback();
}
if (name == VARS) {
return new VarsHash(env);
}
if (name == LOCALE) {
return new SimpleString(env.getLocale().toString());
}
if (name == LOCALE_OBJECT) {
return env.getObjectWrapper().wrap(env.getLocale());
}
if (name == LANG) {
return new SimpleString(env.getLocale().getLanguage());
}
if (name == NODE) {
return env.getCurrentVisitorNode();
}
if (name == MAIN_TEMPLATE_NAME) {
return SimpleString.newInstanceOrNull(env.getMainTemplate().getLookupName());
}
if (name == CURRENT_TEMPLATE_NAME) {
return SimpleString.newInstanceOrNull(env.getCurrentTemplate().getLookupName());
}
if (name == PASS) {
return ASTDirMacroOrFunction.PASS_MACRO;
}
if (name == OUTPUT_ENCODING) {
Charset encoding = env.getOutputEncoding();
return encoding != null ? new SimpleString(encoding.name()) : null;
}
if (name == URL_ESCAPING_CHARSET) {
Charset charset = env.getURLEscapingCharset();
return charset != null ? new SimpleString(charset.name()) : null;
}
if (name == ERROR) {
return new SimpleString(env.getCurrentRecoveredErrorMessage());
}
if (name == NOW) {
return new SimpleDate(new Date(), TemplateDateModel.DATE_TIME);
}
if (name == VERSION) {
return new SimpleString(Configuration.getVersion().toString());
}
if (name == INCOMPATIBLE_IMPROVEMENTS) {
return new SimpleString(env.getConfiguration().getIncompatibleImprovements().toString());
}
if (name == GET_OPTIONAL_TEMPLATE) {
return GetOptionalTemplateFunction.INSTANCE;
}
if (name == CALLER_TEMPLATE_NAME) {
Context ctx = env.getCurrentMacroContext();
if (ctx == null) {
// TODO [FM3] Adjust error message if the special variable syntax is not `.someName` anymore.
throw new TemplateException(
"Can't get ." + CALLER_TEMPLATE_NAME + " here, as there's no macro or function (that's "
+ "implemented in the template) call in context.");
}
String name = ctx.callPlace.getTemplate().getLookupName();
return name != null ? new SimpleString(name) : TemplateStringModel.EMPTY_STRING;
}
if (name == TIME_ZONE) {
return new SimpleString(env.getTimeZone().getID());
}
throw new TemplateException(this,
"Invalid special variable: ", name);
}
@Override
public String getCanonicalForm() {
return "." + name;
}
@Override
public String getLabelWithoutParameters() {
return getCanonicalForm();
}
@Override
boolean isLiteral() {
return false;
}
@Override
ASTExpression deepCloneWithIdentifierReplaced_inner(
String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) {
return this;
}
static class VarsHash implements TemplateHashModel {
Environment env;
VarsHash(Environment env) {
this.env = env;
}
@Override
public TemplateModel get(String key) throws TemplateException {
return env.getVariable(key);
}
}
@Override
int getParameterCount() {
return 0;
}
@Override
Object getParameterValue(int idx) {
throw new IndexOutOfBoundsException();
}
@Override
ParameterRole getParameterRole(int idx) {
throw new IndexOutOfBoundsException();
}
}