blob: 807b26bfcef6fb424dab228eb9e875fd088b836a [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.Arrays;
import java.util.Date;
import freemarker.template.Configuration;
import freemarker.template.SimpleDate;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template._TemplateAPI;
import freemarker.template.utility.StringUtil;
/**
* A reference to a built-in identifier, such as .root
*/
final class BuiltinVariable extends Expression {
static final String TEMPLATE_NAME_CC = "templateName";
static final String TEMPLATE_NAME = "template_name";
static final String MAIN_TEMPLATE_NAME_CC = "mainTemplateName";
static final String MAIN_TEMPLATE_NAME = "main_template_name";
static final String CURRENT_TEMPLATE_NAME_CC = "currentTemplateName";
static final String CURRENT_TEMPLATE_NAME = "current_template_name";
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_CC = "dataModel";
static final String DATA_MODEL = "data_model";
static final String LANG = "lang";
static final String LOCALE = "locale";
static final String LOCALE_OBJECT_CC = "localeObject";
static final String LOCALE_OBJECT = "locale_object";
static final String CURRENT_NODE_CC = "currentNode";
static final String CURRENT_NODE = "current_node";
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_CC = "incompatibleImprovements";
static final String INCOMPATIBLE_IMPROVEMENTS = "incompatible_improvements";
static final String ERROR = "error";
static final String OUTPUT_ENCODING_CC = "outputEncoding";
static final String OUTPUT_ENCODING = "output_encoding";
static final String OUTPUT_FORMAT_CC = "outputFormat";
static final String OUTPUT_FORMAT = "output_format";
static final String AUTO_ESC_CC = "autoEsc";
static final String AUTO_ESC = "auto_esc";
static final String URL_ESCAPING_CHARSET_CC = "urlEscapingCharset";
static final String URL_ESCAPING_CHARSET = "url_escaping_charset";
static final String NOW = "now";
private static final BoundCallable PASS_VALUE = new BoundCallable(UnboundCallable.NO_OP_MACRO, null, null);
static final String[] SPEC_VAR_NAMES = new String[] {
AUTO_ESC_CC,
AUTO_ESC,
CURRENT_NODE_CC,
CURRENT_TEMPLATE_NAME_CC,
CURRENT_NODE,
CURRENT_TEMPLATE_NAME,
DATA_MODEL_CC,
DATA_MODEL,
ERROR,
GLOBALS,
INCOMPATIBLE_IMPROVEMENTS_CC,
INCOMPATIBLE_IMPROVEMENTS,
LANG,
LOCALE,
LOCALE_OBJECT_CC,
LOCALE_OBJECT,
LOCALS,
MAIN,
MAIN_TEMPLATE_NAME_CC,
MAIN_TEMPLATE_NAME,
NAMESPACE,
NODE,
NOW,
OUTPUT_ENCODING_CC,
OUTPUT_FORMAT_CC,
OUTPUT_ENCODING,
OUTPUT_FORMAT,
PASS,
TEMPLATE_NAME_CC,
TEMPLATE_NAME,
URL_ESCAPING_CHARSET_CC,
URL_ESCAPING_CHARSET,
VARS,
VERSION
};
private final String name;
private final TemplateModel parseTimeValue;
BuiltinVariable(Token nameTk, FMParserTokenManager tokenManager, TemplateModel parseTimeValue)
throws ParseException {
String name = nameTk.image;
this.parseTimeValue = parseTimeValue;
if (Arrays.binarySearch(SPEC_VAR_NAMES, name) < 0) {
StringBuilder sb = new StringBuilder();
sb.append("Unknown special variable name: ");
sb.append(StringUtil.jQuote(name)).append(".");
int shownNamingConvention;
{
int namingConvention = tokenManager.namingConvention;
shownNamingConvention = namingConvention != Configuration.AUTO_DETECT_NAMING_CONVENTION
? namingConvention : Configuration.LEGACY_NAMING_CONVENTION /* [2.4] CAMEL_CASE */;
}
{
String correctName;
if (name.equals("auto_escape") || name.equals("auto_escaping") || name.equals("autoesc")) {
correctName = "auto_esc";
} else if (name.equals("autoEscape") || name.equals("autoEscaping")) {
correctName = "autoEsc";
} else {
correctName = null;
}
if (correctName != null) {
sb.append(" You may meant: ");
sb.append(StringUtil.jQuote(correctName)).append(".");
}
}
sb.append("\nThe allowed special variable names are: ");
boolean first = true;
for (int i = 0; i < SPEC_VAR_NAMES.length; i++) {
final String correctName = SPEC_VAR_NAMES[i];
int correctNameNamingConvetion = _CoreStringUtils.getIdentifierNamingConvention(correctName);
if (shownNamingConvention == Configuration.CAMEL_CASE_NAMING_CONVENTION
? correctNameNamingConvetion != Configuration.LEGACY_NAMING_CONVENTION
: correctNameNamingConvetion != Configuration.CAMEL_CASE_NAMING_CONVENTION) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(correctName);
}
}
throw new ParseException(sb.toString(), (UnboundTemplate) 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.getGlobalVariables();
}
if (name == LOCALS) {
CallableInvocationContext ctx = env.getCurrentMacroContext();
return ctx == null ? null : ctx.getLocals();
}
if (name == DATA_MODEL || name == DATA_MODEL_CC) {
return env.getDataModel();
}
if (name == VARS) {
return new VarsHash(env);
}
if (name == LOCALE) {
return new SimpleScalar(env.getLocale().toString());
}
if (name == LOCALE_OBJECT || name == LOCALE_OBJECT_CC) {
return env.getObjectWrapper().wrap(env.getLocale());
}
if (name == LANG) {
return new SimpleScalar(env.getLocale().getLanguage());
}
if (name == CURRENT_NODE || name == NODE || name == CURRENT_NODE_CC) {
return env.getCurrentVisitorNode();
}
if (name == TEMPLATE_NAME || name == TEMPLATE_NAME_CC) {
// The behavior of env.getTemplate() was changed with IcI 2.3.22, but there was an unintended side effect
// of changing the behavior of .template_name, which was fixed with IcI 2.3.23. IcI 2.3.22 deliberately
// remains broken.
return (env.getConfiguration().getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_23)
? new SimpleScalar(env.getTemplate230().getName())
: new SimpleScalar(env.getTemplate().getName());
}
if (name == MAIN_TEMPLATE_NAME || name == MAIN_TEMPLATE_NAME_CC) {
return SimpleScalar.newInstanceOrNull(env.getMainTemplate().getName());
}
if (name == CURRENT_TEMPLATE_NAME || name == CURRENT_TEMPLATE_NAME_CC) {
return SimpleScalar.newInstanceOrNull(env.getCurrentTemplate().getName());
}
if (name == PASS) {
return PASS_VALUE;
}
if (name == OUTPUT_ENCODING || name == OUTPUT_ENCODING_CC) {
String s = env.getOutputEncoding();
return SimpleScalar.newInstanceOrNull(s);
}
if (name == URL_ESCAPING_CHARSET || name == URL_ESCAPING_CHARSET_CC) {
String s = env.getURLEscapingCharset();
return SimpleScalar.newInstanceOrNull(s);
}
if (name == ERROR) {
return new SimpleScalar(env.getCurrentRecoveredErrorMessage());
}
if (name == NOW) {
return new SimpleDate(new Date(), TemplateDateModel.DATETIME);
}
if (name == VERSION) {
return new SimpleScalar(Configuration.getVersionNumber());
}
if (name == INCOMPATIBLE_IMPROVEMENTS || name == INCOMPATIBLE_IMPROVEMENTS_CC) {
return new SimpleScalar(env.getConfiguration().getIncompatibleImprovements().toString());
}
throw new _MiscTemplateException(this,
"Invalid special variable: ", name);
}
@Override
public String toString() {
return "." + name;
}
@Override
public String getCanonicalForm() {
return "." + name;
}
@Override
String getNodeTypeSymbol() {
return getCanonicalForm();
}
@Override
boolean isLiteral() {
return false;
}
@Override
protected Expression deepCloneWithIdentifierReplaced_inner(
String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
return this;
}
static class VarsHash implements TemplateHashModel {
Environment env;
VarsHash(Environment env) {
this.env = env;
}
public TemplateModel get(String key) throws TemplateModelException {
return env.getVariable(key);
}
public boolean isEmpty() {
return false;
}
}
@Override
int getParameterCount() {
return 0;
}
@Override
Object getParameterValue(int idx) {
throw new IndexOutOfBoundsException();
}
@Override
ParameterRole getParameterRole(int idx) {
throw new IndexOutOfBoundsException();
}
}