http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonSequenceException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonSequenceException.java b/src/main/java/org/apache/freemarker/core/NonSequenceException.java new file mode 100644 index 0000000..abe72a0 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonSequenceException.java @@ -0,0 +1,64 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateSequenceModel; + +/** + * Indicates that a {@link TemplateSequenceModel} value was expected, but the value had a different type. + * + * @since 2.3.21 + */ +public class NonSequenceException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateSequenceModel.class }; + + public NonSequenceException(Environment env) { + super(env, "Expecting sequence value here"); + } + + public NonSequenceException(String description, Environment env) { + super(env, description); + } + + NonSequenceException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonSequenceException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "sequence", EXPECTED_TYPES, env); + } + + NonSequenceException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "sequence", EXPECTED_TYPES, tip, env); + } + + NonSequenceException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "sequence", EXPECTED_TYPES, tips, env); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java b/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java new file mode 100644 index 0000000..9b532e9 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java @@ -0,0 +1,92 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.WrapperTemplateModel; +import org.apache.freemarker.core.util._CollectionUtil; + +/** + * Indicates that a {@link TemplateSequenceModel} or {@link TemplateCollectionModel} value was expected, but the value + * had a different type. + * + * @since 2.3.21 + */ +public class NonSequenceOrCollectionException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { + TemplateSequenceModel.class, TemplateCollectionModel.class + }; + private static final String ITERABLE_SUPPORT_HINT = "The problematic value is a java.lang.Iterable. Using " + + "DefaultObjectWrapper(..., iterableSupport=true) as the object_wrapper setting of the FreeMarker " + + "configuration should solve this."; + + public NonSequenceOrCollectionException(Environment env) { + super(env, "Expecting sequence or collection value here"); + } + + public NonSequenceOrCollectionException(String description, Environment env) { + super(env, description); + } + + NonSequenceOrCollectionException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonSequenceOrCollectionException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + this(blamed, model, _CollectionUtil.EMPTY_OBJECT_ARRAY, env); + } + + NonSequenceOrCollectionException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + this(blamed, model, new Object[] { tip }, env); + } + + NonSequenceOrCollectionException( + ASTExpression blamed, TemplateModel model, Object[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "sequence or collection", EXPECTED_TYPES, extendTipsIfIterable(model, tips), env); + } + + private static Object[] extendTipsIfIterable(TemplateModel model, Object[] tips) { + if (isWrappedIterable(model)) { + final int tipsLen = tips != null ? tips.length : 0; + Object[] extendedTips = new Object[tipsLen + 1]; + for (int i = 0; i < tipsLen; i++) { + extendedTips[i] = tips[i]; + } + extendedTips[tipsLen] = ITERABLE_SUPPORT_HINT; + return extendedTips; + } else { + return tips; + } + } + + public static boolean isWrappedIterable(TemplateModel model) { + return model instanceof WrapperTemplateModel + && ((WrapperTemplateModel) model).getWrappedObject() instanceof Iterable; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonStringException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonStringException.java b/src/main/java/org/apache/freemarker/core/NonStringException.java new file mode 100644 index 0000000..1c0ff8b --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonStringException.java @@ -0,0 +1,74 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateBooleanModel; +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateNumberModel; +import org.apache.freemarker.core.model.TemplateScalarModel; + +/** + * Indicates that a {@link TemplateScalarModel} value was expected (or maybe something that can be automatically coerced + * to that), but the value had a different type. + */ +public class NonStringException extends UnexpectedTypeException { + + static final String STRING_COERCABLE_TYPES_DESC + = "string or something automatically convertible to string (number, date or boolean)"; + + static final Class[] STRING_COERCABLE_TYPES = new Class[] { + TemplateScalarModel.class, TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class + }; + + private static final String DEFAULT_DESCRIPTION + = "Expecting " + NonStringException.STRING_COERCABLE_TYPES_DESC + " value here"; + + public NonStringException(Environment env) { + super(env, DEFAULT_DESCRIPTION); + } + + public NonStringException(String description, Environment env) { + super(env, description); + } + + NonStringException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonStringException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, env); + } + + NonStringException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, tip, env); + } + + NonStringException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java b/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java new file mode 100644 index 0000000..73454e6 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java @@ -0,0 +1,77 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateScalarModel; + +/** + * Indicates that a {@link TemplateScalarModel} (or maybe something that can be automatically coerced + * to that) or {@link TemplateMarkupOutputModel} value was expected, but the value had a different type. + */ +public class NonStringOrTemplateOutputException extends UnexpectedTypeException { + + static final String STRING_COERCABLE_TYPES_OR_TOM_DESC + = NonStringException.STRING_COERCABLE_TYPES_DESC + ", or \"template output\" "; + + static final Class[] STRING_COERCABLE_TYPES_AND_TOM; + static { + STRING_COERCABLE_TYPES_AND_TOM = new Class[NonStringException.STRING_COERCABLE_TYPES.length + 1]; + int i; + for (i = 0; i < NonStringException.STRING_COERCABLE_TYPES.length; i++) { + STRING_COERCABLE_TYPES_AND_TOM[i] = NonStringException.STRING_COERCABLE_TYPES[i]; + } + STRING_COERCABLE_TYPES_AND_TOM[i] = TemplateMarkupOutputModel.class; + }; + + private static final String DEFAULT_DESCRIPTION + = "Expecting " + NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC + " value here"; + + public NonStringOrTemplateOutputException(Environment env) { + super(env, DEFAULT_DESCRIPTION); + } + + public NonStringOrTemplateOutputException(String description, Environment env) { + super(env, description); + } + + NonStringOrTemplateOutputException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonStringOrTemplateOutputException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, env); + } + + NonStringOrTemplateOutputException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, tip, env); + } + + NonStringOrTemplateOutputException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonUserDefinedDirectiveLikeException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonUserDefinedDirectiveLikeException.java b/src/main/java/org/apache/freemarker/core/NonUserDefinedDirectiveLikeException.java new file mode 100644 index 0000000..fe83c12 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonUserDefinedDirectiveLikeException.java @@ -0,0 +1,67 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateDirectiveModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateTransformModel; + +/** + * Indicates that a {@link TemplateDirectiveModel} or {@link TemplateTransformModel} or {@link ASTDirMacro} value was + * expected, but the value had a different type. + * + * @since 2.3.21 + */ +class NonUserDefinedDirectiveLikeException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { + TemplateDirectiveModel.class, TemplateTransformModel.class, ASTDirMacro.class }; + + public NonUserDefinedDirectiveLikeException(Environment env) { + super(env, "Expecting user-defined directive, transform or macro value here"); + } + + public NonUserDefinedDirectiveLikeException(String description, Environment env) { + super(env, description); + } + + NonUserDefinedDirectiveLikeException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonUserDefinedDirectiveLikeException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, env); + } + + NonUserDefinedDirectiveLikeException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, tip, env); + } + + NonUserDefinedDirectiveLikeException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java b/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java new file mode 100644 index 0000000..50e79d9 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java @@ -0,0 +1,156 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.freemarker.core.util._ClassUtil; +import org.apache.freemarker.core.util._StringUtil; + +/** + * A {@link TemplateClassResolver} that resolves only the classes whose name + * was specified in the constructor. + */ +public class OptInTemplateClassResolver implements TemplateClassResolver { + + private final Set/*<String>*/ allowedClasses; + private final List/*<String>*/ trustedTemplatePrefixes; + private final Set/*<String>*/ trustedTemplateNames; + + /** + * Creates a new instance. + * + * @param allowedClasses the {@link Set} of {@link String}-s that contains + * the full-qualified names of the allowed classes. + * Can be <code>null</code> (means not class is allowed). + * @param trustedTemplates the {@link List} of {@link String}-s that contains + * template names (i.e., template root directory relative paths) + * and prefix patterns (like <code>"include/*"</code>) of templates + * for which {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} will be + * used (which is not as safe as {@link OptInTemplateClassResolver}). + * The list items need not start with <code>"/"</code> (if they are, it + * will be removed). List items ending with <code>"*"</code> are treated + * as prefixes (i.e. <code>"foo*"</code> matches <code>"foobar"</code>, + * <code>"foo/bar/baaz"</code>, <code>"foowhatever/bar/baaz"</code>, + * etc.). The <code>"*"</code> has no special meaning anywhere else. + * The matched template name is the name (template root directory + * relative path) of the template that directly (lexically) contains the + * operation (like <code>?new</code>) that wants to get the class. Thus, + * if a trusted template includes a non-trusted template, the + * <code>allowedClasses</code> restriction will apply in the included + * template. + * This parameter can be <code>null</code> (means no trusted templates). + */ + public OptInTemplateClassResolver( + Set allowedClasses, List<String> trustedTemplates) { + this.allowedClasses = allowedClasses != null ? allowedClasses : Collections.EMPTY_SET; + if (trustedTemplates != null) { + trustedTemplateNames = new HashSet(); + trustedTemplatePrefixes = new ArrayList(); + + Iterator<String> it = trustedTemplates.iterator(); + while (it.hasNext()) { + String li = it.next(); + if (li.startsWith("/")) li = li.substring(1); + if (li.endsWith("*")) { + trustedTemplatePrefixes.add(li.substring(0, li.length() - 1)); + } else { + trustedTemplateNames.add(li); + } + } + } else { + trustedTemplateNames = Collections.EMPTY_SET; + trustedTemplatePrefixes = Collections.EMPTY_LIST; + } + } + + @Override + public Class resolve(String className, Environment env, Template template) + throws TemplateException { + String templateName = safeGetTemplateName(template); + + if (templateName != null + && (trustedTemplateNames.contains(templateName) + || hasMatchingPrefix(templateName))) { + return TemplateClassResolver.UNRESTRICTED_RESOLVER.resolve(className, env, template); + } else { + if (!allowedClasses.contains(className)) { + throw new _MiscTemplateException(env, + "Instantiating ", className, " is not allowed in the template for security reasons. (If you " + + "run into this problem when using ?new in a template, you may want to check the \"", + Configurable.NEW_BUILTIN_CLASS_RESOLVER_KEY, + "\" setting in the FreeMarker configuration.)"); + } else { + try { + return _ClassUtil.forName(className); + } catch (ClassNotFoundException e) { + throw new _MiscTemplateException(e, env); + } + } + } + } + + /** + * Extract the template name from the template object which will be matched + * against the trusted template names and pattern. + */ + protected String safeGetTemplateName(Template template) { + if (template == null) return null; + + String name = template.getName(); + if (name == null) return null; + + // Detect exploits, return null if one is suspected: + String decodedName = name; + if (decodedName.indexOf('%') != -1) { + decodedName = _StringUtil.replace(decodedName, "%2e", ".", false, false); + decodedName = _StringUtil.replace(decodedName, "%2E", ".", false, false); + decodedName = _StringUtil.replace(decodedName, "%2f", "/", false, false); + decodedName = _StringUtil.replace(decodedName, "%2F", "/", false, false); + decodedName = _StringUtil.replace(decodedName, "%5c", "\\", false, false); + decodedName = _StringUtil.replace(decodedName, "%5C", "\\", false, false); + } + int dotDotIdx = decodedName.indexOf(".."); + if (dotDotIdx != -1) { + int before = dotDotIdx - 1 >= 0 ? decodedName.charAt(dotDotIdx - 1) : -1; + int after = dotDotIdx + 2 < decodedName.length() ? decodedName.charAt(dotDotIdx + 2) : -1; + if ((before == -1 || before == '/' || before == '\\') + && (after == -1 || after == '/' || after == '\\')) { + return null; + } + } + + return name.startsWith("/") ? name.substring(1) : name; + } + + private boolean hasMatchingPrefix(String name) { + for (int i = 0; i < trustedTemplatePrefixes.size(); i++) { + String prefix = (String) trustedTemplatePrefixes.get(i); + if (name.startsWith(prefix)) return true; + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/OutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/OutputFormat.java b/src/main/java/org/apache/freemarker/core/OutputFormat.java new file mode 100644 index 0000000..7c16c5b --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/OutputFormat.java @@ -0,0 +1,83 @@ +/* + * 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 org.apache.freemarker.core.util._ClassUtil; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents an output format. If you need auto-escaping, see its subclass, {@link MarkupOutputFormat}. + * + * @see Configuration#setOutputFormat(OutputFormat) + * @see Configuration#setRegisteredCustomOutputFormats(java.util.Collection) + * @see MarkupOutputFormat + * + * @since 2.3.24 + */ +public abstract class OutputFormat { + + /** + * The short name used to refer to this format (like in the {@code #ftl} header). + */ + public abstract String getName(); + + /** + * Returns the MIME type of the output format. This might comes handy when generating a HTTP response. {@code null} + * if this output format doesn't clearly corresponds to a specific MIME type. + */ + public abstract String getMimeType(); + + /** + * Tells if this output format allows inserting {@link TemplateMarkupOutputModel}-s of another output formats into + * it. If {@code true}, the foreign {@link TemplateMarkupOutputModel} will be inserted into the output as is (like + * if the surrounding output format was the same). This is usually a bad idea allow, as such an event could indicate + * application bugs. If this method returns {@code false} (recommended), then FreeMarker will try to assimilate the + * inserted value by converting its format to this format, which will currently (2.3.24) cause exception, unless the + * inserted value is made by escaping plain text and the target format is non-escaping, in which case format + * conversion is trivially possible. (It's not impossible that conversions will be extended beyond this, if there + * will be demand for that.) + * + * <p> + * {@code true} value is used by {@link UndefinedOutputFormat}. + */ + public abstract boolean isOutputFormatMixingAllowed(); + + /** + * Returns the short description of this format, to be used in error messages. + * Override {@link #toStringExtraProperties()} to customize this. + */ + @Override + public final String toString() { + String extras = toStringExtraProperties(); + return getName() + "(" + + "mimeType=" + _StringUtil.jQuote(getMimeType()) + ", " + + "class=" + _ClassUtil.getShortClassNameOfObject(this, true) + + (extras.length() != 0 ? ", " : "") + extras + + ")"; + } + + /** + * Should be like {@code "foo=\"something\", bar=123"}; this will be inserted inside the parentheses in + * {@link #toString()}. Shouldn't return {@code null}; should return {@code ""} if there are no extra properties. + */ + protected String toStringExtraProperties() { + return ""; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/OutputFormatBoundBuiltIn.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/OutputFormatBoundBuiltIn.java b/src/main/java/org/apache/freemarker/core/OutputFormatBoundBuiltIn.java new file mode 100644 index 0000000..f0a38d3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/OutputFormatBoundBuiltIn.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util._NullArgumentException; + +abstract class OutputFormatBoundBuiltIn extends SpecialBuiltIn { + + protected OutputFormat outputFormat; + protected int autoEscapingPolicy; + + void bindToOutputFormat(OutputFormat outputFormat, int autoEscapingPolicy) { + _NullArgumentException.check(outputFormat); + this.outputFormat = outputFormat; + this.autoEscapingPolicy = autoEscapingPolicy; + } + + @Override + TemplateModel _eval(Environment env) throws TemplateException { + if (outputFormat == null) { + // The parser should prevent this situation + throw new NullPointerException("outputFormat was null"); + } + return calculateResult(env); + } + + protected abstract TemplateModel calculateResult(Environment env) + throws TemplateException; + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ParameterRole.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParameterRole.java b/src/main/java/org/apache/freemarker/core/ParameterRole.java new file mode 100644 index 0000000..07c5046 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ParameterRole.java @@ -0,0 +1,93 @@ +/* + * 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; + +// Change this to an Enum in Java 5 +/** + * @see ASTNode#getParameterRole(int) + */ +final class ParameterRole { + + private final String name; + + static final ParameterRole UNKNOWN = new ParameterRole("[unknown role]"); + + // When figuring out the names of these, always read them after the possible getName() values. It should sound OK. + // Like "`+` left hand operand", or "`#if` parameter". That is, the roles (only) have to make sense in the + // context of the possible ASTNode classes. + static final ParameterRole LEFT_HAND_OPERAND = new ParameterRole("left-hand operand"); + static final ParameterRole RIGHT_HAND_OPERAND = new ParameterRole("right-hand operand"); + static final ParameterRole ENCLOSED_OPERAND = new ParameterRole("enclosed operand"); + static final ParameterRole ITEM_VALUE = new ParameterRole("item value"); + static final ParameterRole ITEM_KEY = new ParameterRole("item key"); + static final ParameterRole ASSIGNMENT_TARGET = new ParameterRole("assignment target"); + static final ParameterRole ASSIGNMENT_OPERATOR = new ParameterRole("assignment operator"); + static final ParameterRole ASSIGNMENT_SOURCE = new ParameterRole("assignment source"); + static final ParameterRole VARIABLE_SCOPE = new ParameterRole("variable scope"); + static final ParameterRole NAMESPACE = new ParameterRole("namespace"); + static final ParameterRole ERROR_HANDLER = new ParameterRole("error handler"); + static final ParameterRole PASSED_VALUE = new ParameterRole("passed value"); + static final ParameterRole CONDITION = new ParameterRole("condition"); + static final ParameterRole VALUE = new ParameterRole("value"); + static final ParameterRole AST_NODE_SUBTYPE = new ParameterRole("AST-node subtype"); + static final ParameterRole PLACEHOLDER_VARIABLE = new ParameterRole("placeholder variable"); + static final ParameterRole EXPRESSION_TEMPLATE = new ParameterRole("expression template"); + static final ParameterRole LIST_SOURCE = new ParameterRole("list source"); + static final ParameterRole TARGET_LOOP_VARIABLE = new ParameterRole("target loop variable"); + static final ParameterRole TEMPLATE_NAME = new ParameterRole("template name"); + static final ParameterRole PARSE_PARAMETER = new ParameterRole("\"parse\" parameter"); + static final ParameterRole ENCODING_PARAMETER = new ParameterRole("\"encoding\" parameter"); + static final ParameterRole IGNORE_MISSING_PARAMETER = new ParameterRole("\"ignore_missing\" parameter"); + static final ParameterRole PARAMETER_NAME = new ParameterRole("parameter name"); + static final ParameterRole PARAMETER_DEFAULT = new ParameterRole("parameter default"); + static final ParameterRole CATCH_ALL_PARAMETER_NAME = new ParameterRole("catch-all parameter name"); + static final ParameterRole ARGUMENT_NAME = new ParameterRole("argument name"); + static final ParameterRole ARGUMENT_VALUE = new ParameterRole("argument value"); + static final ParameterRole CONTENT = new ParameterRole("content"); + static final ParameterRole EMBEDDED_TEMPLATE = new ParameterRole("embedded template"); + static final ParameterRole VALUE_PART = new ParameterRole("value part"); + static final ParameterRole MINIMUM_DECIMALS = new ParameterRole("minimum decimals"); + static final ParameterRole MAXIMUM_DECIMALS = new ParameterRole("maximum decimals"); + static final ParameterRole NODE = new ParameterRole("node"); + static final ParameterRole CALLEE = new ParameterRole("callee"); + static final ParameterRole MESSAGE = new ParameterRole("message"); + + private ParameterRole(String name) { + this.name = name; + } + + static ParameterRole forBinaryOperatorOperand(int paramIndex) { + switch (paramIndex) { + case 0: return LEFT_HAND_OPERAND; + case 1: return RIGHT_HAND_OPERAND; + default: throw new IndexOutOfBoundsException(); + } + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ParseException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParseException.java b/src/main/java/org/apache/freemarker/core/ParseException.java new file mode 100644 index 0000000..2b5eef2 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ParseException.java @@ -0,0 +1,513 @@ +/* + * 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.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.freemarker.core.FMParserConstants; +import org.apache.freemarker.core.Token; +import org.apache.freemarker.core.util._SecurityUtil; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Parsing-time exception in a template (as opposed to a runtime exception, a {@link TemplateException}). This usually + * signals syntactical/lexical errors. + * + * Note that on JavaCC-level lexical errors throw {@link TokenMgrError} instead of this, however with the API-s that + * most users use those will be wrapped into {@link ParseException}-s. + * + * This is a modified version of file generated by JavaCC from FTL.jj. + * You can modify this class to customize the error reporting mechanisms so long as the public interface + * remains compatible with the original. + * + * @see TokenMgrError + */ +public class ParseException extends IOException implements FMParserConstants { + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * following this token will (therefore) be the first error token. + */ + public Token currentToken; + + private static volatile Boolean jbossToolsMode; + + private boolean messageAndDescriptionRendered; + private String message; + private String description; + + public int columnNumber, lineNumber; + public int endColumnNumber, endLineNumber; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * The end of line string for this machine. + */ + protected String eol = _SecurityUtil.getSystemProperty("line.separator", "\n"); + + private String templateName; + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: <result of getMessage> + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) { + super(""); + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + lineNumber = currentToken.next.beginLine; + columnNumber = currentToken.next.beginColumn; + endLineNumber = currentToken.next.endLine; + endColumnNumber = currentToken.next.endColumn; + } + + /** + * Used by JavaCC generated code. + */ + protected ParseException() { + super(); + } + + /** + * @since 2.3.21 + */ + public ParseException(String description, Template template, + int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber) { + this(description, template, lineNumber, columnNumber, endLineNumber, endColumnNumber, null); + } + + /** + * @since 2.3.21 + */ + public ParseException(String description, Template template, + int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, + Throwable cause) { + this(description, + template == null ? null : template.getSourceName(), + lineNumber, columnNumber, + endLineNumber, endColumnNumber, + cause); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, Template template, Token tk) { + this(description, template, tk, null); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, Template template, Token tk, Throwable cause) { + this(description, + template == null ? null : template.getSourceName(), + tk.beginLine, tk.beginColumn, + tk.endLine, tk.endColumn, + cause); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, ASTNode tobj) { + this(description, tobj, null); + } + + /** + * @since 2.3.20 + */ + public ParseException(String description, ASTNode tobj, Throwable cause) { + this(description, + tobj.getTemplate() == null ? null : tobj.getTemplate().getSourceName(), + tobj.beginLine, tobj.beginColumn, + tobj.endLine, tobj.endColumn, + cause); + } + + private ParseException(String description, String templateName, + int lineNumber, int columnNumber, + int endLineNumber, int endColumnNumber, + Throwable cause) { + super(description); // but we override getMessage, so it will be different + try { + initCause(cause); + } catch (Exception e) { + // Suppressed; we can't do more + } + this.description = description; + this.templateName = templateName; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.endLineNumber = endLineNumber; + this.endColumnNumber = endColumnNumber; + } + + /** + * Should be used internally only; sets the name of the template that contains the error. + * This is needed as the constructor that JavaCC automatically calls doesn't pass in the template, so we + * set it somewhere later in an exception handler. + */ + public void setTemplateName(String templateName) { + this.templateName = templateName; + synchronized (this) { + messageAndDescriptionRendered = false; + message = null; + } + } + + /** + * Returns the error location plus the error description. + * + * @see #getDescription() + * @see #getTemplateName() + * @see #getLineNumber() + * @see #getColumnNumber() + */ + @Override + public String getMessage() { + synchronized (this) { + if (messageAndDescriptionRendered) return message; + } + renderMessageAndDescription(); + synchronized (this) { + return message; + } + } + + private String getDescription() { + synchronized (this) { + if (messageAndDescriptionRendered) return description; + } + renderMessageAndDescription(); + synchronized (this) { + return description; + } + } + + /** + * Returns the description of the error without error location or source quotations, or {@code null} if there's no + * description available. This is useful in editors (IDE-s) where the error markers and the editor window itself + * already carry this information, so it's redundant the repeat in the error dialog. + */ + public String getEditorMessage() { + return getDescription(); + } + + /** + * Returns the name (template-root relative path) of the template whose parsing was failed. + * Maybe {@code null} if this is a non-stored template. + * + * @since 2.3.20 + */ + public String getTemplateName() { + return templateName; + } + + /** + * 1-based line number of the failing section, or 0 is the information is not available. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * 1-based column number of the failing section, or 0 is the information is not available. + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * 1-based line number of the last line that contains the failing section, or 0 if the information is not available. + * + * @since 2.3.21 + */ + public int getEndLineNumber() { + return endLineNumber; + } + + /** + * 1-based column number of the last character of the failing section, or 0 if the information is not available. + * Note that unlike with Java string API-s, this column number is inclusive. + * + * @since 2.3.21 + */ + public int getEndColumnNumber() { + return endColumnNumber; + } + + private void renderMessageAndDescription() { + String desc = getOrRenderDescription(); + + String prefix; + if (!isInJBossToolsMode()) { + prefix = "Syntax error " + + MessageUtil.formatLocationForSimpleParsingError(templateName, lineNumber, columnNumber) + + ":\n"; + } else { + prefix = "[col. " + columnNumber + "] "; + } + + String msg = prefix + desc; + desc = msg.substring(prefix.length()); // so we reuse the backing char[] + + synchronized (this) { + message = msg; + description = desc; + messageAndDescriptionRendered = true; + } + } + + private boolean isInJBossToolsMode() { + if (jbossToolsMode == null) { + try { + jbossToolsMode = Boolean.valueOf( + ParseException.class.getClassLoader().toString().indexOf( + "[org.jboss.ide.eclipse.freemarker:") != -1); + } catch (Throwable e) { + jbossToolsMode = Boolean.FALSE; + } + } + return jbossToolsMode.booleanValue(); + } + + /** + * Returns the description of the error without the error location, or {@code null} if there's no description + * available. + */ + private String getOrRenderDescription() { + synchronized (this) { + if (description != null) return description; // When we already have it from the constructor + } + + String tokenErrDesc; + if (currentToken != null) { + tokenErrDesc = getCustomTokenErrorDescription(); + if (tokenErrDesc == null) { + // The default JavaCC message generation stuff follows. + StringBuilder expected = new StringBuilder(); + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (i != 0) { + expected.append(eol); + } + expected.append(" "); + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + if (j != 0) expected.append(' '); + expected.append(tokenImage[expectedTokenSequences[i][j]]); + } + } + tokenErrDesc = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) tokenErrDesc += " "; + if (tok.kind == 0) { + tokenErrDesc += tokenImage[0]; + break; + } + tokenErrDesc += add_escapes(tok.image); + tok = tok.next; + } + tokenErrDesc += "\", but "; + + if (expectedTokenSequences.length == 1) { + tokenErrDesc += "was expecting:" + eol; + } else { + tokenErrDesc += "was expecting one of:" + eol; + } + tokenErrDesc += expected; + } + } else { + tokenErrDesc = null; + } + return tokenErrDesc; + } + + private String getCustomTokenErrorDescription() { + final Token nextToken = currentToken.next; + final int kind = nextToken.kind; + if (kind == EOF) { + Set/*<String>*/ endNames = new HashSet(); + for (int[] sequence : expectedTokenSequences) { + for (int aSequence : sequence) { + switch (aSequence) { + case END_FOREACH: + endNames.add("#foreach"); + break; + case END_LIST: + endNames.add("#list"); + break; + case END_SWITCH: + endNames.add("#switch"); + break; + case END_IF: + endNames.add("#if"); + break; + case END_COMPRESS: + endNames.add("#compress"); + break; + case END_MACRO: + endNames.add("#macro"); + case END_FUNCTION: + endNames.add("#function"); + break; + case END_TRANSFORM: + endNames.add("#transform"); + break; + case END_ESCAPE: + endNames.add("#escape"); + break; + case END_NOESCAPE: + endNames.add("#noescape"); + break; + case END_ASSIGN: + endNames.add("#assign"); + break; + case END_LOCAL: + endNames.add("#local"); + break; + case END_GLOBAL: + endNames.add("#global"); + break; + case END_ATTEMPT: + endNames.add("#attempt"); + break; + case CLOSING_CURLY_BRACKET: + endNames.add("\"{\""); + break; + case CLOSE_BRACKET: + endNames.add("\"[\""); + break; + case CLOSE_PAREN: + endNames.add("\"(\""); + break; + case UNIFIED_CALL_END: + endNames.add("@..."); + break; + } + } + } + return "Unexpected end of file reached." + + (endNames.size() == 0 ? "" : " You have an unclosed " + concatWithOrs(endNames) + "."); + } else if (kind == ELSE) { + return "Unexpected directive, \"#else\". " + + "Check if you have a valid #if-#elseif-#else or #list-#else structure."; + } else if (kind == END_IF || kind == ELSE_IF) { + return "Unexpected directive, " + + _StringUtil.jQuote(nextToken) + + ". Check if you have a valid #if-#elseif-#else structure."; + } + return null; + } + + private String concatWithOrs(Set/*<String>*/ endNames) { + StringBuilder sb = new StringBuilder(); + for (Iterator/*<String>*/ it = endNames.iterator(); it.hasNext(); ) { + String endName = (String) it.next(); + if (sb.length() != 0) { + sb.append(" or "); + } + sb.append(endName); + } + return sb.toString(); + } + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuilder retval = new StringBuilder(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParserConfiguration.java b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java new file mode 100644 index 0000000..24b6a1d --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ParserConfiguration.java @@ -0,0 +1,77 @@ +/* + * 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; + +/** + * <b>Don't implement this interface yourself</b>; use the existing implementation(s). This interface is implemented by + * classes that hold settings that affect parsing. New parser settings can be added in new FreeMarker versions, which + * will break your implementation. + * + * @since 2.3.24 + */ +public interface ParserConfiguration { + + /** + * See {@link Configuration#getTagSyntax()}. + */ + int getTagSyntax(); + + /** + * See {@link Configuration#getNamingConvention()}. + */ + int getNamingConvention(); + + /** + * See {@link Configuration#getWhitespaceStripping()}. + */ + boolean getWhitespaceStripping(); + + /** + * Overlaps with {@link Configurable#getArithmeticEngine()}; the parser needs this for creating numerical literals. + */ + ArithmeticEngine getArithmeticEngine(); + + /** + * See {@link Configuration#getAutoEscapingPolicy()}. + */ + int getAutoEscapingPolicy(); + + /** + * See {@link Configuration#getOutputEncoding()}. + */ + OutputFormat getOutputFormat(); + + /** + * See {@link Configuration#getRecognizeStandardFileExtensions()}. + */ + boolean getRecognizeStandardFileExtensions(); + + /** + * See {@link Configuration#getIncompatibleImprovements()}. + */ + Version getIncompatibleImprovements(); + + /** + * See {@link Configuration#getTabSize()}. + * + * @since 2.3.25 + */ + int getTabSize(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ParsingNotSupportedException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ParsingNotSupportedException.java b/src/main/java/org/apache/freemarker/core/ParsingNotSupportedException.java new file mode 100644 index 0000000..bac087a --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ParsingNotSupportedException.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * Thrown when the {@link TemplateValueFormat} doesn't support parsing, and parsing was invoked. + * + * @since 2.3.24 + */ +public class ParsingNotSupportedException extends TemplateValueFormatException { + + public ParsingNotSupportedException(String message, Throwable cause) { + super(message, cause); + } + + public ParsingNotSupportedException(String message) { + this(message, null); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/PlainTextOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/PlainTextOutputFormat.java b/src/main/java/org/apache/freemarker/core/PlainTextOutputFormat.java new file mode 100644 index 0000000..3a58c56 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/PlainTextOutputFormat.java @@ -0,0 +1,56 @@ +/* + * 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; + +/** + * Represents the plain text output format (MIME type "text/plain", name "plainText"). This format doesn't support + * escaping. This format doesn't allow mixing in template output values of other output formats. + * + * <p> + * The main difference from {@link UndefinedOutputFormat} is that this format doesn't allow inserting values of another + * output format into itself (unless they can be converted to plain text), while {@link UndefinedOutputFormat} would + * just insert the foreign "markup" as is. Also, this format has {"text/plain"} MIME type, while + * {@link UndefinedOutputFormat} has {@code null}. + * + * @since 2.3.24 + */ +public final class PlainTextOutputFormat extends OutputFormat { + + public static final PlainTextOutputFormat INSTANCE = new PlainTextOutputFormat(); + + private PlainTextOutputFormat() { + // Only to decrease visibility + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + + @Override + public String getName() { + return "plainText"; + } + + @Override + public String getMimeType() { + return "text/plain"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/RTFOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/RTFOutputFormat.java b/src/main/java/org/apache/freemarker/core/RTFOutputFormat.java new file mode 100644 index 0000000..3c00eef --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/RTFOutputFormat.java @@ -0,0 +1,75 @@ +/* + * 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.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the Rich Text Format output format (MIME type "application/rtf", name "RTF"). This format escapes by + * default (via {@link _StringUtil#RTFEnc(String)}). The {@code ?rtf} built-in silently bypasses template output values + * of the type produced by this output format ({@link TemplateRTFOutputModel}). + * + * @since 2.3.24 + */ +public final class RTFOutputFormat extends CommonMarkupOutputFormat<TemplateRTFOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final RTFOutputFormat INSTANCE = new RTFOutputFormat(); + + private RTFOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "RTF"; + } + + @Override + public String getMimeType() { + return "application/rtf"; + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + _StringUtil.RTFEnc(textToEsc, out); + } + + @Override + public String escapePlainText(String plainTextContent) { + return _StringUtil.RTFEnc(plainTextContent); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) { + return builtInName.equals("rtf"); + } + + @Override + protected TemplateRTFOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateRTFOutputModel(plainTextContent, markupContent); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/RangeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/RangeModel.java b/src/main/java/org/apache/freemarker/core/RangeModel.java new file mode 100644 index 0000000..d0e2a48 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/RangeModel.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.impl.SimpleNumber; + +abstract class RangeModel implements TemplateSequenceModel, java.io.Serializable { + + private final int begin; + + public RangeModel(int begin) { + this.begin = begin; + } + + final int getBegining() { + return begin; + } + + @Override + final public TemplateModel get(int index) throws TemplateModelException { + if (index < 0 || index >= size()) { + throw new _TemplateModelException("Range item index ", Integer.valueOf(index), " is out of bounds."); + } + long value = begin + getStep() * (long) index; + return value <= Integer.MAX_VALUE ? new SimpleNumber((int) value) : new SimpleNumber(value); + } + + /** + * @return {@code 1} or {@code -1}; other return values need not be properly handled until FTL supports other steps. + */ + abstract int getStep(); + + abstract boolean isRightUnbounded(); + + abstract boolean isRightAdaptive(); + + abstract boolean isAffactedByStringSlicingBug(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/RegexpHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/RegexpHelper.java b/src/main/java/org/apache/freemarker/core/RegexpHelper.java new file mode 100644 index 0000000..1c865a3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/RegexpHelper.java @@ -0,0 +1,207 @@ +/* + * 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.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.templateresolver.impl.MruCacheStorage; +import org.apache.freemarker.core.util._StringUtil; +import org.slf4j.Logger; + +/** + * Helper for language features (like built-ins) that use regular expressions. + */ +final class RegexpHelper { + + private static final Logger LOG = _CoreLogs.RUNTIME; + + private static volatile boolean flagWarningsEnabled = LOG.isWarnEnabled(); + private static final int MAX_FLAG_WARNINGS_LOGGED = 25; + private static final Object flagWarningsCntSync = new Object(); + private static int flagWarningsCnt; + + private static final MruCacheStorage patternCache = new MruCacheStorage(50, 150); + + static private long intFlagToLong(int flag) { + return flag & 0x0000FFFFL; + } + + // Standard regular expression flags converted to long: + static final long RE_FLAG_CASE_INSENSITIVE = intFlagToLong(Pattern.CASE_INSENSITIVE); + + static final long RE_FLAG_MULTILINE = intFlagToLong(Pattern.MULTILINE); + + static final long RE_FLAG_COMMENTS = intFlagToLong(Pattern.COMMENTS); + + static final long RE_FLAG_DOTALL = intFlagToLong(Pattern.DOTALL); + + // FreeMarker-specific regular expression flags (using the higher 32 bits): + static final long RE_FLAG_REGEXP = 0x100000000L; + + static final long RE_FLAG_FIRST_ONLY = 0x200000000L; + + // Can't be instantiated + private RegexpHelper() { } + + static Pattern getPattern(String patternString, int flags) + throws TemplateModelException { + PatternCacheKey patternKey = new PatternCacheKey(patternString, flags); + + Pattern result; + + synchronized (patternCache) { + result = (Pattern) patternCache.get(patternKey); + } + if (result != null) { + return result; + } + + try { + result = Pattern.compile(patternString, flags); + } catch (PatternSyntaxException e) { + throw new _TemplateModelException(e, + "Malformed regular expression: ", new _DelayedGetMessage(e)); + } + synchronized (patternCache) { + patternCache.put(patternKey, result); + } + return result; + }; + + private static class PatternCacheKey { + private final String patternString; + private final int flags; + private final int hashCode; + + public PatternCacheKey(String patternString, int flags) { + this.patternString = patternString; + this.flags = flags; + hashCode = patternString.hashCode() + 31 * flags; + } + + @Override + public boolean equals(Object that) { + if (that instanceof PatternCacheKey) { + PatternCacheKey thatPCK = (PatternCacheKey) that; + return thatPCK.flags == flags + && thatPCK.patternString.equals(patternString); + } else { + return false; + } + } + + @Override + public int hashCode() { + return hashCode; + } + + } + + static long parseFlagString(String flagString) { + long flags = 0; + for (int i = 0; i < flagString.length(); i++) { + char c = flagString.charAt(i); + switch (c) { + case 'i': + flags |= RE_FLAG_CASE_INSENSITIVE; + break; + case 'm': + flags |= RE_FLAG_MULTILINE; + break; + case 'c': + flags |= RE_FLAG_COMMENTS; + break; + case 's': + flags |= RE_FLAG_DOTALL; + break; + case 'r': + flags |= RE_FLAG_REGEXP; + break; + case 'f': + flags |= RE_FLAG_FIRST_ONLY; + break; + default: + if (flagWarningsEnabled) { + // [FM3] Should be an error + RegexpHelper.logFlagWarning( + "Unrecognized regular expression flag: " + + _StringUtil.jQuote(String.valueOf(c)) + "."); + } + } // switch + } + return flags; + } + + /** + * Logs flag warning for a limited number of times. This is used to prevent + * log flooding. + */ + static void logFlagWarning(String message) { + if (!flagWarningsEnabled) return; + + int cnt; + synchronized (flagWarningsCntSync) { + cnt = flagWarningsCnt; + if (cnt < MAX_FLAG_WARNINGS_LOGGED) { + flagWarningsCnt++; + } else { + flagWarningsEnabled = false; + return; + } + } + message += " This will be an error in some later FreeMarker version!"; + if (cnt + 1 == MAX_FLAG_WARNINGS_LOGGED) { + message += " [Will not log more regular expression flag problems until restart!]"; + } + LOG.warn(message); + } + + static void checkNonRegexpFlags(String biName, long flags) throws _TemplateModelException { + checkOnlyHasNonRegexpFlags(biName, flags, false); + } + + static void checkOnlyHasNonRegexpFlags(String biName, long flags, boolean strict) + throws _TemplateModelException { + if (!strict && !flagWarningsEnabled) return; + + String flag; + if ((flags & RE_FLAG_MULTILINE) != 0) { + flag = "m"; + } else if ((flags & RE_FLAG_DOTALL) != 0) { + flag = "s"; + } else if ((flags & RE_FLAG_COMMENTS) != 0) { + flag = "c"; + } else { + return; + } + + final Object[] msg = { "?", biName ," doesn't support the \"", flag, "\" flag " + + "without the \"r\" flag." }; + if (strict) { + throw new _TemplateModelException(msg); + } else { + // Suppress error for backward compatibility + logFlagWarning(new _ErrorDescriptionBuilder(msg).toString()); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java b/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java new file mode 100644 index 0000000..c856cbc --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java @@ -0,0 +1,48 @@ +/* + * 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; + +abstract class RightUnboundedRangeModel extends RangeModel { + + RightUnboundedRangeModel(int begin) { + super(begin); + } + + @Override + final int getStep() { + return 1; + } + + @Override + final boolean isRightUnbounded() { + return true; + } + + @Override + final boolean isRightAdaptive() { + return true; + } + + @Override + final boolean isAffactedByStringSlicingBug() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/SpecialBuiltIn.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/SpecialBuiltIn.java b/src/main/java/org/apache/freemarker/core/SpecialBuiltIn.java new file mode 100644 index 0000000..7e839a2 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/SpecialBuiltIn.java @@ -0,0 +1,27 @@ +/* + * 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; + + +/** + * Marker class for built-ins that has special treatment during parsing. + */ +abstract class SpecialBuiltIn extends ASTExpBuiltIn { + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/StopException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/StopException.java b/src/main/java/org/apache/freemarker/core/StopException.java new file mode 100644 index 0000000..dd9bc41 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/StopException.java @@ -0,0 +1,64 @@ +/* + * 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.io.PrintStream; +import java.io.PrintWriter; + +/** + * This exception is thrown when a <tt>#stop</tt> directive is encountered. + */ +public class StopException extends TemplateException { + + StopException(Environment env) { + super(env); + } + + StopException(Environment env, String s) { + super(s, env); + } + + @Override + public void printStackTrace(PrintWriter pw) { + synchronized (pw) { + String msg = getMessage(); + pw.print("Encountered stop instruction"); + if (msg != null && !msg.equals("")) { + pw.println("\nCause given: " + msg); + } else pw.println(); + super.printStackTrace(pw); + } + } + + @Override + public void printStackTrace(PrintStream ps) { + synchronized (ps) { + String msg = getMessage(); + ps.print("Encountered stop instruction"); + if (msg != null && !msg.equals("")) { + ps.println("\nCause given: " + msg); + } else ps.println(); + super.printStackTrace(ps); + } + } + +} + + http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/Template.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/Template.java b/src/main/java/org/apache/freemarker/core/Template.java index 113f9ea..48996ac 100644 --- a/src/main/java/org/apache/freemarker/core/Template.java +++ b/src/main/java/org/apache/freemarker/core/Template.java @@ -36,21 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; -import org.apache.freemarker.core.ast.BugException; -import org.apache.freemarker.core.ast.Configurable; -import org.apache.freemarker.core.ast.Environment; -import org.apache.freemarker.core.ast.FMParser; -import org.apache.freemarker.core.ast.LibraryLoad; -import org.apache.freemarker.core.ast.Macro; -import org.apache.freemarker.core.ast.OutputFormat; -import org.apache.freemarker.core.ast.ParseException; -import org.apache.freemarker.core.ast.ParserConfiguration; -import org.apache.freemarker.core.ast.TemplateConfiguration; -import org.apache.freemarker.core.ast.TemplateElement; -import org.apache.freemarker.core.ast.TemplateSpecifiedEncodingHandler; -import org.apache.freemarker.core.ast.TextBlock; -import org.apache.freemarker.core.ast.TokenMgrError; -import org.apache.freemarker.core.ast._CoreAPI; +import org.apache.freemarker.core.FMParser; import org.apache.freemarker.core.debug.impl.DebuggerService; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateHashModel; @@ -60,6 +46,7 @@ import org.apache.freemarker.core.model.impl.SimpleHash; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; +import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._NullArgumentException; /** @@ -94,7 +81,7 @@ public class Template extends Configurable { private Map macros = new HashMap(); private List imports = new Vector(); - private TemplateElement rootElement; + private _ASTElement rootElement; private String encoding, defaultNS; private Object customLookupCondition; private int actualTagSyntax; @@ -331,7 +318,7 @@ public class Template extends Configurable { } catch (IOException e) { throw new BugException("Plain text template creation failed", e); } - _CoreAPI.replaceText((TextBlock) template.rootElement, content); + _CoreAPI.replaceText((ASTStaticText) template.rootElement, content); DebuggerService.registerTemplate(template); return template; } @@ -402,7 +389,7 @@ public class Template extends Configurable { } /** - * Creates a {@link org.apache.freemarker.core.ast.Environment Environment} object, using this template, the data-model provided as + * Creates a {@link org.apache.freemarker.core.Environment Environment} object, using this template, the data-model provided as * parameter. You have to call {@link Environment#process()} on the return value to set off the actual rendering. * * <p>Use this method if you want to do some special initialization on the {@link Environment} before template @@ -708,7 +695,7 @@ public class Template extends Configurable { * @deprecated Should only be used internally, and might will be removed later. */ @Deprecated - public void addMacro(Macro macro) { + public void addMacro(ASTDirMacro macro) { macros.put(macro.getName(), macro); } @@ -718,7 +705,7 @@ public class Template extends Configurable { * @deprecated Should only be used internally, and might will be removed later. */ @Deprecated - public void addImport(LibraryLoad ll) { + public void addImport(ASTDirImport ll) { imports.add(ll); } @@ -734,7 +721,7 @@ public class Template extends Configurable { * @param endColumn the last column of the requested source, 1-based * @param endLine the last line of the requested source, 1-based * - * @see org.apache.freemarker.core.ast.TemplateObject#getSource() + * @see org.apache.freemarker.core.ASTNode#getSource() */ public String getSource(int beginColumn, int beginLine, @@ -874,7 +861,7 @@ public class Template extends Configurable { * @deprecated Should only be used internally, and might will be removed later. */ @Deprecated - public TemplateElement getRootTreeNode() { + public _ASTElement getRootTreeNode() { return rootElement; } @@ -978,17 +965,17 @@ public class Template extends Configurable { } /** - * @return an array of the {@link TemplateElement}s containing the given column and line numbers. + * @return an array of the {@link _ASTElement}s containing the given column and line numbers. * @deprecated Should only be used internally, and might will be removed later. */ @Deprecated public List containingElements(int column, int line) { final ArrayList elements = new ArrayList(); - TemplateElement element = rootElement; + _ASTElement element = rootElement; mainloop: while (element.contains(column, line)) { elements.add(element); for (Enumeration enumeration = element.children(); enumeration.hasMoreElements(); ) { - TemplateElement elem = (TemplateElement) enumeration.nextElement(); + _ASTElement elem = (_ASTElement) enumeration.nextElement(); if (elem.contains(column, line)) { element = elem; continue mainloop;
