All Downloads are FREE. Search and download functionalities are using the official Maven repository.

freemarker.template.MethodCallAwareTemplateHashModel Maven / Gradle / Ivy

There is a newer version: 2.3.32_1
Show newest version
/*
 * 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.template;

import java.util.Collection;
import java.util.Map;

import freemarker.core.Macro;
import freemarker.core.NonMethodException;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.ZeroArgumentNonVoidMethodPolicy;
import freemarker.template.utility.NullArgumentException;

/**
 * Adds an extra getter method to {@link TemplateHashModel} that can return different result than {@link #get(String)},
 * knowing that the result of it will be called as a method. At least as of 2.3.33, this is only used by the
 * template language for 0-argument method calls that are directly after the dot operator and the key (like in
 * {@code obj.m()}, where the "()" is directly after the key "m"), or for the equivalent of that with square brackets
 * ({@code obj["m"]()}).
 *
 * 

Background knowledge needed to understand this: In the FreeMarker template language, methods/functions are * first class values (just like strings, numbers,etc.). Also, unlike in Java, there's no separate namespace for * methods, and for the other field-like members. When you have {@code obj.m()} in a template, first, the dot operator * gets the value for the key "m", and after that, and independently of that, the method call operator tries to call * that value (and if it's not a method or function, that will fail). The dot operator, before 2.3.33, was never aware * of what the value it gets will be used for (like will it be called, will it be printed, etc.). Now it can be. * * For example, if in the template you have {@code someRecord.someComponent()}, and there {@code someRecord} was wrapped * by the {@link ObjectWrapper} into a {@link TemplateHashModel} that also implements this interface, then the dot * operator will call {@link #getBeforeMethodCall(String) getBeforeMethodCall("someComponent")}, rather than * {@link #get(String) get("someComponent")}. This is needed to implement subtle features like * {@link BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)}, * which is needed to implement * {@link ZeroArgumentNonVoidMethodPolicy#BOTH_METHOD_AND_PROPERTY_UNLESS_BEAN_PROPERTY_READ_METHOD}. * *

While technically we could do the same for method calls with more the 0 arguments, as of 2.3.33 at least, we * don't want to generalize this to that case. This is a workaround we added to address the issue with accessing * components in Java records, which are 0-argument methods (see that at * {@link BeansWrapper.MethodAppearanceDecision#setMethodInsteadOfPropertyValueBeforeCall(boolean)}). * *

Objects wrapped with {@link BeansWrapper}, and hence with {@link DefaultObjectWrapper}, will implement this * interface when they are "generic" objects (that is, when they are not classes with special wrapper, like * {@link Map}-s, {@link Collection}-s, {@link Number}-s, etc.). * * @since 2.3.33 */ public interface MethodCallAwareTemplateHashModel extends TemplateHashModel { /** * This is called instead of {@link #get(String)} if we know that the return value should be callable like a method. * The advantage of this is that we can coerce the value to a method when desirable, and otherwise can give * a more specific error message in the resulting exception than the standard {@link NonMethodException} would. * * @param key * Same as for {@link #get(String)} * * @return * Same as for {@link #get(String)}, except it should return a {@link TemplateMethodModel} * (a {@link TemplateMethodModelEx} if possible), or in very rare case a {@link Macro} that was created with * the {@code function} directive. Or, {@code null} in the same case as * {@link #get(String)}. The method should never return something that's not callable in the template language * as a method or function; the return type is {@link TemplateModel} because that's the most specific common * super-interface of {@link TemplateMethodModel}, and {@link Macro}. * * @throws ShouldNotBeGetAsMethodException * If the value for the given key exists, but shouldn't be coerced to something callable as a method. This will * be converted to {@link NonMethodException} by the engine, but in this exception you can optionally give a * more specific explanation, and that will be added to the resulting {@link NonMethodException} as a hint to * the user. */ TemplateModel getBeforeMethodCall(String key) throws TemplateModelException, ShouldNotBeGetAsMethodException; /** * Thrown by {@link #getBeforeMethodCall(String)}; see there. * * @since 2.3.33 */ final class ShouldNotBeGetAsMethodException extends Exception { private final TemplateModel actualValue; private final String hint; /** * Same as {@link ShouldNotBeGetAsMethodException(TemplateModel, String, Throwable)}, with {@code null} * cause exception argument. */ public ShouldNotBeGetAsMethodException(TemplateModel actualValue, String hint) { this(actualValue, hint, null); } /** * @param actualValue * The actual value we got instead of a method; can't be {@code null}! * @param hint * Hint for the user, that's added to the error message; {@code null} if you just want the plain * {@link NonMethodException} error message. * @param cause * Can be {@code null}. */ public ShouldNotBeGetAsMethodException(TemplateModel actualValue, String hint, Throwable cause) { super(null, cause, true, false); NullArgumentException.check(actualValue); this.actualValue = actualValue; this.hint = hint; } /** * The actual value we got instead of a method; not {@code null}. */ public TemplateModel getActualValue() { return actualValue; } /** * Additional hint for the user; maybe {@code null}. */ public String getHint() { return hint; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy