You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/14 10:53:21 UTC
[38/51] [partial] incubator-freemarker git commit: Migrated from Ant
to Gradle, and modularized the project. This is an incomplete migration;
there are some TODO-s in the build scripts, and release related tasks are
still missing. What works: Building th
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() {
+ * @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...@foo>},
+ * {@code <@foo>Name: <#if showIt>Joe</#...@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();
+
+}