http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationException.java
new file mode 100644
index 0000000..5b61cca
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationException.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;
+
+/**
+ * Error while configuring FreeMarker.
+ */
+@SuppressWarnings("serial")
+public class ConfigurationException extends RuntimeException {
+
+    public ConfigurationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ConfigurationException(String message) {
+        super(message);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
new file mode 100644
index 0000000..3ed6512
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
@@ -0,0 +1,86 @@
+/*
+ * 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.Date;
+
+import org.apache.freemarker.core.Configuration.ExtendableBuilder;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * Thrown by {@link ExtendableBuilder#setSetting(String, String)}; The setting 
name was recognized, but its value
+ * couldn't be parsed or the setting couldn't be set for some other reason. 
This exception should always have a
+ * cause exception.
+ */
+@SuppressWarnings("serial")
+public class ConfigurationSettingValueException extends ConfigurationException 
{
+
+    public ConfigurationSettingValueException(String name, String value, 
Throwable cause) {
+        this(name, value, true, null, cause);
+    }
+
+    public ConfigurationSettingValueException(String name, String value, 
String reason) {
+        this(name, value, true, reason, null);
+    }
+
+    /**
+     * @param name
+     *         The name of the setting
+     * @param value
+     *         The value of the setting
+     * @param showValue
+     *         Whether the value of the setting should be shown in the error 
message. Set to {@code false} if you want
+     *         to avoid {@link #toString()}-ing the {@code value}.
+     * @param reason
+     *         The explanation of why setting the setting has failed; maybe 
{@code null}, especially if you have a cause
+     *         exception anyway.
+     * @param cause
+     *         The cause exception of this exception (why setting the setting 
was failed)
+     */
+    public ConfigurationSettingValueException(String name, Object value, 
boolean showValue, String reason,
+            Throwable cause) {
+        super(
+                createMessage(
+                    name, value, true,
+                    reason != null ? ", because: " : (cause != null ? "; see 
cause exception." : null),
+                    reason),
+                cause);
+    }
+
+    private static String createMessage(String name, Object value, boolean 
showValue, String detail1, String detail2) {
+        StringBuilder sb = new StringBuilder(64);
+        sb.append("Failed to set FreeMarker configuration setting 
").append(_StringUtil.jQuote(name));
+        if (showValue) {
+            sb.append(" to value ")
+                    .append(
+                            value instanceof Number || value instanceof 
Boolean || value instanceof Date ? value
+                            : _StringUtil.jQuote(value));
+        } else {
+            sb.append(" to the specified value");
+        }
+        if (detail1 != null) {
+            sb.append(detail1);
+        }
+        if (detail2 != null) {
+            sb.append(detail2);
+        }
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateKey.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateKey.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateKey.java
new file mode 100644
index 0000000..fedf096
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateKey.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * Used with {@link CustomStateScope}-s; each subclass must have exactly one 
instance, which should be stored in
+ * a static final field. So the usual usage is like this:
+ *
+ * <pre>
+ *     static final CustomStateKey MY_STATE = new CustomStateKey() {
+ *         &#x40;Override
+ *         protected Object create() {
+ *             return new ...;
+ *         }
+ *     };
+ * </pre>
+ */
+public abstract class CustomStateKey<T> {
+
+    /**
+     * This will be invoked when the state for this {@link CustomStateKey} is 
get via {@link
+     * CustomStateScope#getCustomState(CustomStateKey)}, but it doesn't yet 
exists in the given scope. Then the created
+     * object will be stored in the scope and then it's returned. Must not 
return {@code null}.
+     */
+    protected abstract T create();
+
+    /**
+     * Does identity comparison (like operator {@code ==}).
+     */
+    @Override
+    final public boolean equals(Object o) {
+        return o == this;
+    }
+
+    /**
+     * Returns {@link Object#hashCode()}.
+     */
+    @Override
+    final public int hashCode() {
+        return super.hashCode();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateScope.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateScope.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateScope.java
new file mode 100644
index 0000000..4067823
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/CustomStateScope.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * An object that's a scope that can store custom state objects.
+ */
+public interface CustomStateScope {
+
+    /**
+     * Gets the custom state belonging to the key, automatically creating it 
if it doesn't yet exists in the scope.
+     * If the scope is {@link Configuration} or {@link Template}, then this 
method is thread safe. If the scope is
+     * {@link Environment}, then this method is not thread safe ({@link 
Environment} is not thread safe either).
+     */
+    <T> T getCustomState(CustomStateKey<T> customStateKey);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
new file mode 100644
index 0000000..5793ad3
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
@@ -0,0 +1,137 @@
+/*
+ * 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.IdentityHashMap;
+
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateTransformModel;
+import org.apache.freemarker.core.util.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 {@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>
+ * Currently this method doesn't give you access to the {@link Template} 
object, because it's probable that future
+ * versions of FreeMarker will be able to use the same parsed representation 
of a "file" for multiple {@link Template}
+ * objects. Then the call place will be bound to the parsed representation, 
not to the {@link Template} objects that are
+ * based on it.
+ * 
+ * <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 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.
+     */
+    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 ({@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 ({@code </@...>}), then it 
points to the last character of that.
+     */
+    int getEndLine();
+
+    /**
+     * 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 invoke 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
+     * {@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();
+
+}

Reply via email to