freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [41/54] [partial] incubator-freemarker git commit: Unifying the o.a.f.core and o.a.f.core.ast
Date Thu, 23 Feb 2017 21:36:08 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java b/src/main/java/org/apache/freemarker/core/Configuration.java
index 49573ae..8e4cfd1 100644
--- a/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -40,31 +40,6 @@ import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import org.apache.freemarker.core.ast.BugException;
-import org.apache.freemarker.core.ast.CSSOutputFormat;
-import org.apache.freemarker.core.ast.CombinedMarkupOutputFormat;
-import org.apache.freemarker.core.ast.Configurable;
-import org.apache.freemarker.core.ast.Environment;
-import org.apache.freemarker.core.ast.HTMLOutputFormat;
-import org.apache.freemarker.core.ast.JSONOutputFormat;
-import org.apache.freemarker.core.ast.JavaScriptOutputFormat;
-import org.apache.freemarker.core.ast.MarkupOutputFormat;
-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.PlainTextOutputFormat;
-import org.apache.freemarker.core.ast.RTFOutputFormat;
-import org.apache.freemarker.core.ast.TemplateConfiguration;
-import org.apache.freemarker.core.ast.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.ast.UndefinedOutputFormat;
-import org.apache.freemarker.core.ast.UnregisteredOutputFormatException;
-import org.apache.freemarker.core.ast.XHTMLOutputFormat;
-import org.apache.freemarker.core.ast.XMLOutputFormat;
-import org.apache.freemarker.core.ast._CoreAPI;
-import org.apache.freemarker.core.ast._DelayedJQuote;
-import org.apache.freemarker.core.ast._MiscTemplateException;
-import org.apache.freemarker.core.ast._ObjectBuilderSettingEvaluator;
-import org.apache.freemarker.core.ast._SettingEvaluationEnvironment;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -89,6 +64,7 @@ import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader;
 import org.apache.freemarker.core.templateresolver.impl.MruCacheStorage;
 import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader;
 import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage;
+import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util.CaptureOutput;
 import org.apache.freemarker.core.util.HtmlEscape;
 import org.apache.freemarker.core.util.NormalizeNewlines;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/CustomAttribute.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/CustomAttribute.java b/src/main/java/org/apache/freemarker/core/CustomAttribute.java
new file mode 100644
index 0000000..3dee398
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/CustomAttribute.java
@@ -0,0 +1,264 @@
+/*
+ * 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.BugException;
+
+/**
+ * A class that allows one to associate custom data with a {@link Configuration}, a {@link
Template}, or
+ * {@link Environment}.
+ * 
+ * <p>This API has similar approach to that of {@link ThreadLocal} (which allows one
to associate
+ * custom data with a thread). With an example:</p>
+ * 
+ * <pre>
+ * // The object identity itself will serve as the attribute identifier; there's no attribute
name String:
+ * public static final CustomAttribute MY_ATTR = new CustomAttribute(CustomAttribute.SCOPE_CONFIGURATION);
+ * ...
+ *     // Set the attribute in this particular Configuration object:
+ *     MY_ATTR.set(myAttrValue, cfg);
+ *     ...
+ *     // Read the attribute from this particular Configuration object:
+ *     myAttrValue = MY_ATTR.get(cfg);
+ * </pre>
+ */
+// [2.4] Use generics; type parameter used for the type of the stored value 
+public class CustomAttribute {
+    
+    /**
+     * Constant used in the constructor specifying that this attribute is {@link Environment}-scoped.
+     */
+    public static final int SCOPE_ENVIRONMENT = 0;
+        
+    /**
+     * Constant used in the constructor specifying that this attribute is {@link Template}-scoped.
+     */
+    public static final int SCOPE_TEMPLATE = 1;
+        
+    /**
+     * Constant used in the constructor specifying that this attribute is {@link Configuration}-scoped.
+     */
+    public static final int SCOPE_CONFIGURATION = 2;
+
+    // We use an internal key instead of 'this' so that malicious subclasses 
+    // overriding equals() and hashCode() can't gain access to other attribute
+    // values. That's also the reason why get() and set() are marked final.
+    private final Object key = new Object();
+    private final int scope;
+    
+    /**
+     * Creates a new custom attribute with the specified scope
+     * @param scope one of <tt>SCOPE_</tt> constants. 
+     */
+    public CustomAttribute(int scope) {
+        if (scope != SCOPE_ENVIRONMENT && 
+           scope != SCOPE_TEMPLATE && 
+           scope != SCOPE_CONFIGURATION) {
+                throw new IllegalArgumentException();
+            }
+        this.scope = scope;
+    }
+    
+    /**
+     * This method is invoked when {@link #get()} is invoked without 
+     * {@link #set(Object)} being invoked before it to define the value in the 
+     * current scope. Override it to create the attribute value on-demand.  
+     * @return the initial value for the custom attribute. By default returns null.
+     */
+    protected Object create() {
+        return null;
+    }
+    
+    /**
+     * Gets the attribute from the appropriate scope that's accessible through the specified
{@link Environment}. If
+     * the attribute has {@link #SCOPE_ENVIRONMENT} scope, it will be get from the given
{@link Environment} directly.
+     * If the attribute has {@link #SCOPE_TEMPLATE} scope, it will be get from the parent
of the given
+     * {@link Environment} (that is, in {@link Environment#getParent()}) directly). If the
attribute has
+     * {@link #SCOPE_CONFIGURATION} scope, it will be get from {@link Environment#getConfiguration()}.
+     * 
+     * @throws NullPointerException
+     *             If {@code env} is null
+     * 
+     * @return The new value of the attribute (possibly {@code null}), or {@code null} if
the attribute doesn't exist.
+     * 
+     * @since 2.3.22
+     */
+    public final Object get(Environment env) {
+        return getScopeConfigurable(env).getCustomAttribute(key, this);
+    }
+
+    /**
+     * Same as {@link #get(Environment)}, but uses {@link Environment#getCurrentEnvironment()}
to fill the 2nd argument.
+     * 
+     * @throws IllegalStateException
+     *             If there is no current {@link Environment}, which is usually the case
when the current thread isn't
+     *             processing a template.
+     */
+    public final Object get() {
+        return getScopeConfigurable(getRequiredCurrentEnvironment()).getCustomAttribute(key,
this);
+    }
+    
+    /**
+     * Gets the value of a {@link Template}-scope attribute from the given {@link Template}.
+     * 
+     * @throws UnsupportedOperationException
+     *             If this custom attribute has different scope than {@link #SCOPE_TEMPLATE}.
+     * @throws NullPointerException
+     *             If {@code template} is null
+     */
+    public final Object get(Template template) {
+        if (scope != SCOPE_TEMPLATE) {
+            throw new UnsupportedOperationException("This is not a template-scope attribute");
+        }
+        return ((Configurable) template).getCustomAttribute(key, this);
+    }
+    
+    /**
+     * Same as {@link #get(Template)}, but applies to a {@link TemplateConfiguration}.  
+     * 
+     * @since 2.3.24
+     */
+    public Object get(TemplateConfiguration templateConfiguration) {
+        if (scope != SCOPE_TEMPLATE) {
+            throw new UnsupportedOperationException("This is not a template-scope attribute");
+        }
+        return templateConfiguration.getCustomAttribute(key, this);
+    }
+    
+    /**
+     * Gets the value of a {@link Configuration}-scope attribute from the given {@link Configuration}.
+     * 
+     * @throws UnsupportedOperationException
+     *             If this custom attribute has different scope than {@link #SCOPE_CONFIGURATION}.
+     * @throws NullPointerException
+     *             If {@code cfg} is null
+     * 
+     * @since 2.3.22
+     */
+    public final Object get(Configuration cfg) {
+        if (scope != SCOPE_CONFIGURATION) {
+            throw new UnsupportedOperationException("This is not a template-scope attribute");
+        }
+        return ((Configurable) cfg).getCustomAttribute(key, this);
+    }
+    
+    /**
+     * Sets the attribute inside the appropriate scope that's accessible through the specified
{@link Environment}. If
+     * the attribute has {@link #SCOPE_ENVIRONMENT} scope, it will be set in the given {@link
Environment} directly. If
+     * the attribute has {@link #SCOPE_TEMPLATE} scope, it will be set in the parent of the
given {@link Environment}
+     * (that is, in {@link Environment#getParent()}) directly). If the attribute has {@link
#SCOPE_CONFIGURATION} scope,
+     * it will be set in {@link Environment#getConfiguration()}.
+     * 
+     * @param value
+     *            The new value of the attribute. Can be {@code null}.
+     * 
+     * @throws NullPointerException
+     *             If {@code env} is null
+     * 
+     * @since 2.3.22
+     */
+    public final void set(Object value, Environment env) {
+        getScopeConfigurable(env).setCustomAttribute(key, value);
+    }
+
+    /**
+     * Same as {@link #set(Object, Environment)}, but uses {@link Environment#getCurrentEnvironment()}
to fill the 2nd
+     * argument.
+     * 
+     * @throws IllegalStateException
+     *             If there is no current {@link Environment}, which is usually the case
when the current thread isn't
+     *             processing a template.
+     */
+    public final void set(Object value) {
+        getScopeConfigurable(getRequiredCurrentEnvironment()).setCustomAttribute(key, value);
+    }
+
+    /**
+     * Sets the value of a {@link Template}-scope attribute in the given {@link Template}.
+     * 
+     * @param value
+     *            The new value of the attribute. Can be {@code null}.
+     * 
+     * @throws UnsupportedOperationException
+     *             If this custom attribute has different scope than {@link #SCOPE_TEMPLATE}.
+     * @throws NullPointerException
+     *             If {@code template} is null
+     */
+    public final void set(Object value, Template template) {
+        if (scope != SCOPE_TEMPLATE) {
+            throw new UnsupportedOperationException("This is not a template-scope attribute");
+        }
+        ((Configurable) template).setCustomAttribute(key, value);
+    }
+
+    /**
+     * Same as {@link #set(Object, Template)}, but applicable to a {@link TemplateConfiguration}.

+     * 
+     * @since 2.3.24
+     */
+    public final void set(Object value, TemplateConfiguration templateConfiguration) {
+        if (scope != SCOPE_TEMPLATE) {
+            throw new UnsupportedOperationException("This is not a template-scope attribute");
+        }
+        templateConfiguration.setCustomAttribute(key, value);
+    }
+    
+    /**
+     * Sets the value of a {@link Configuration}-scope attribute in the given {@link Configuration}.
+     * 
+     * @param value
+     *            The new value of the attribute. Can be {@code null}.
+     * 
+     * @throws UnsupportedOperationException
+     *             If this custom attribute has different scope than {@link #SCOPE_CONFIGURATION}.
+     * @throws NullPointerException
+     *             If {@code cfg} is null
+     * 
+     * @since 2.3.22
+     */
+    public final void set(Object value, Configuration cfg) {
+        if (scope != SCOPE_CONFIGURATION) {
+            throw new UnsupportedOperationException("This is not a configuration-scope attribute");
+        }
+        ((Configurable) cfg).setCustomAttribute(key, value);
+    }
+    
+    private Environment getRequiredCurrentEnvironment() {
+        Environment c = Environment.getCurrentEnvironment();
+        if (c == null) {
+            throw new IllegalStateException("No current environment");
+        }
+        return c;
+    }
+
+    private Configurable getScopeConfigurable(Environment env) throws Error {
+        switch (scope) {
+        case SCOPE_ENVIRONMENT:
+            return env;
+        case SCOPE_TEMPLATE:
+            return env.getParent();
+        case SCOPE_CONFIGURATION:
+            return env.getParent().getParent();
+        default:
+            throw new BugException();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java b/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
new file mode 100644
index 0000000..bce8659
--- /dev/null
+++ b/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 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
+     * {@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();
+
+}


Mime
View raw message