/*
 * 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 freemarker.ext.util.IdentityHashMap;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateTransformModel;
import freemarker.template.utility.ObjectFactory;

Gives information about the place where a directive is called from, also lets you attach a custom data object to that place. Each directive call in a template has its own DirectiveCallPlace object (even when they call the same directive with the same parameters). The life cycle of the DirectiveCallPlace object is bound to the Template object that contains the directive call. Hence, the DirectiveCallPlace object and the custom data you put into it is cached together with the Template (and templates are normally cached - see Configuration.getTemplate(String)). The custom data is normally initialized on demand, that is, when the directive call is first executed, via getOrCreateCustomData(Object, ObjectFactory).

Don't implement this interface yourself, as new methods can be added to it any time! It's only meant to be implemented by the FreeMarker core.

This interface is currently only used for custom directive calls (that is, a <@...> that calls a TemplateDirectiveModel, TemplateTransformModel, or a macro).

See Also:
Since:2.3.22
/** * Gives information about the place where a directive is called from, also lets you attach a custom data object to that * place. Each directive call in a template has its own {@link DirectiveCallPlace} object (even when they call the same * directive with the same parameters). The life cycle of the {@link DirectiveCallPlace} object is bound to the * {@link Template} object that contains the directive call. Hence, the {@link DirectiveCallPlace} object and the custom * data you put into it is cached together with the {@link Template} (and templates are normally cached - see * {@link Configuration#getTemplate(String)}). The custom data is normally initialized on demand, that is, when the * directive call is first executed, via {@link #getOrCreateCustomData(Object, ObjectFactory)}. * * <p> * <b>Don't implement this interface yourself</b>, as new methods can be added to it any time! It's only meant to be * implemented by the FreeMarker core. * * <p> * This interface is currently only used for custom directive calls (that is, a {@code <@...>} that calls a * {@link TemplateDirectiveModel}, {@link TemplateTransformModel}, or a macro). * * @see Environment#getCurrentDirectiveCallPlace() * * @since 2.3.22 */
public interface DirectiveCallPlace {
The template that contains this call; null if the call is not from a template (but directly from user Java code, for example).
Since:2.3.28
/** * The template that contains this call; {@code null} if the call is not from a template (but directly from * user Java code, for example). * * @since 2.3.28 */
Template getTemplate();
The 1-based column number of the first character of the directive call in the template source code, or -1 if it's not known.
/** * The 1-based column number of the first character of the directive call in the template source code, or -1 if it's * not known. */
int getBeginColumn();
The 1-based line number of the first character of the directive call in the template source code, or -1 if it's not known.
/** * The 1-based line number of the first character of the directive call in the template source code, or -1 if it's * not known. */
int getBeginLine();
The 1-based column number of the last character of the directive call in the template source code, or -1 if it's not known. If the directive has an end-tag (</@...>), then it points to the last character of that.
/** * The 1-based column number of the last character of the directive call in the template source code, or -1 if it's * not known. If the directive has an end-tag ({@code </@...>}), then it points to the last character of that. */
int getEndColumn();
The 1-based line number of the last character of the directive call in the template source code, or -1 if it's not known. If the directive has an end-tag (</@...>), then it points to the last character of that.
/** * The 1-based line number of the last character of the directive call in the template source code, or -1 if it's * not known. If the directive has an end-tag ({@code </@...>}), then it points to the last character of that. */
int getEndLine();
Returns the custom data, or if that's null, then it creates and stores it in an atomic operation then returns it. This method is thread-safe, however, it doesn't ensure thread safe (like synchronized) access to the custom data itself. See the top-level documentation of DirectiveCallPlace to understand the scope and life-cycle of the custom data. Be sure that the custom data only depends on things that get their final value during template parsing, not on runtime settings.

This method will block other calls while the objectFactory is executing, thus, the object will be usually created only once, even if multiple threads request the value when it's still null. It doesn't stand though when providerIdentity mismatches occur (see later). Furthermore, then it's also possible that multiple objects created by the same ObjectFactory will be in use on the same time, because of directive executions already running in parallel, and because of memory synchronization delays (hardware dependent) between the threads.

Params:
  • providerIdentity – This is usually the class of the TemplateDirectiveModel that creates (and uses) the custom data, or if you are using your own class for the custom data object (as opposed to a class from some more generic API), then that class. This is needed as the same call place might calls different directives depending on runtime conditions, and so it must be ensured that these directives won't accidentally read each other's custom data, ending up with class cast exceptions or worse. In the current implementation, if there's a providerIdentity mismatch (means, the providerIdentity object used when the custom data was last set isn't the exactly same object as the one provided with the parameter now), the previous custom data will be just ignored as if it was null. So if multiple directives that use the custom data feature use the same call place, the caching of the custom data can be inefficient, as they will keep overwriting each other's custom data. (In a more generic implementation the providerIdentity would be a key in a IdentityHashMap, but then this feature would be slower, while providerIdentity mismatches aren't occurring in most applications.)
  • objectFactory – Called when the custom data wasn't yet set, to create its initial value. If this parameter is null and the custom data wasn't set yet, then null will be returned. The returned value of ObjectFactory.createObject() can be any kind of object, but can't be null.
Throws:
Returns:The current custom data object, or possibly null if there was no ObjectFactory provided.
/** * Returns the custom data, or if that's {@code null}, then it creates and stores it in an atomic operation then * returns it. This method is thread-safe, however, it doesn't ensure thread safe (like synchronized) access to the * custom data itself. See the top-level documentation of {@link DirectiveCallPlace} to understand the scope and * life-cycle of the custom data. Be sure that the custom data only depends on things that get their final value * during template parsing, not on runtime settings. * * <p> * This method will block other calls while the {@code objectFactory} is executing, thus, the object will be * <em>usually</em> created only once, even if multiple threads request the value when it's still {@code null}. It * doesn't stand though when {@code providerIdentity} mismatches occur (see later). Furthermore, then it's also * possible that multiple objects created by the same {@link ObjectFactory} will be in use on the same time, because * of directive executions already running in parallel, and because of memory synchronization delays (hardware * dependent) between the threads. * * @param providerIdentity * This is usually the class of the {@link TemplateDirectiveModel} that creates (and uses) the custom * data, or if you are using your own class for the custom data object (as opposed to a class from some * more generic API), then that class. This is needed as the same call place might calls different * directives depending on runtime conditions, and so it must be ensured that these directives won't * accidentally read each other's custom data, ending up with class cast exceptions or worse. In the * current implementation, if there's a {@code providerIdentity} mismatch (means, the * {@code providerIdentity} object used when the custom data was last set isn't the exactly same object * as the one provided with the parameter now), the previous custom data will be just ignored as if it * was {@code null}. So if multiple directives that use the custom data feature use the same call place, * the caching of the custom data can be inefficient, as they will keep overwriting each other's custom * data. (In a more generic implementation the {@code providerIdentity} would be a key in a * {@link IdentityHashMap}, but then this feature would be slower, while {@code providerIdentity} * mismatches aren't occurring in most applications.) * @param objectFactory * Called when the custom data wasn't yet set, to create its initial value. If this parameter is * {@code null} and the custom data wasn't set yet, then {@code null} will be returned. The returned * value of {@link ObjectFactory#createObject()} can be any kind of object, but can't be {@code null}. * * @return The current custom data object, or possibly {@code null} if there was no {@link ObjectFactory} provided. * * @throws CallPlaceCustomDataInitializationException * If the {@link ObjectFactory} had to be invoked but failed. */
Object getOrCreateCustomData(Object providerIdentity, ObjectFactory objectFactory) throws CallPlaceCustomDataInitializationException;
Tells if the nested content (the body) can be safely cached, as it only depends on the template content (not on variable values and such) and has no side-effects (other than writing to the output). Examples of cases that give false: <@foo>Name: ${name}</@foo>, <@foo>Name: <#if showIt>Joe</#if></@foo>. Examples of cases that give true: <@foo>Name: Joe</@foo>, <@foo />. Note that we get true for no nested content, because that's equivalent with 0-length nested content in FTL.

This method returns a pessimistic result. For example, if it sees a custom directive call, it can't know what it does, so it will assume that it's not cacheable.

/** * Tells if the nested content (the body) can be safely cached, as it only depends on the template content (not on * variable values and such) and has no side-effects (other than writing to the output). Examples of cases that give * {@code false}: {@code <@foo>Name: } <tt>${name}</tt>{@code</@foo>}, * {@code <@foo>Name: <#if showIt>Joe</#if></@foo>}. Examples of cases that give {@code true}: * {@code <@foo>Name: Joe</@foo>}, {@code <@foo />}. Note that we get {@code true} for no nested content, because * that's equivalent with 0-length nested content in FTL. * * <p> * This method returns a pessimistic result. For example, if it sees a custom directive call, it can't know what it * does, so it will assume that it's not cacheable. */
boolean isNestedOutputCacheable(); }