http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XMLOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XMLOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XMLOutputFormat.java new file mode 100644 index 0000000..644f323 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XMLOutputFormat.java @@ -0,0 +1,77 @@ +/* + * 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.outputformat.impl; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat; +import org.apache.freemarker.core.outputformat.OutputFormat; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the XML output format (MIME type "application/xml", name "XML"). This format escapes by default (via + * {@link _StringUtil#XMLEnc(String)}). The {@code ?html}, {@code ?xhtml} and {@code ?xml} built-ins silently bypass + * template output values of the type produced by this output format ({@link TemplateXHTMLOutputModel}). + * + * @since 2.3.24 + */ +public final class XMLOutputFormat extends CommonMarkupOutputFormat<TemplateXMLOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final XMLOutputFormat INSTANCE = new XMLOutputFormat(); + + private XMLOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "XML"; + } + + @Override + public String getMimeType() { + return "application/xml"; + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + _StringUtil.XMLEnc(textToEsc, out); + } + + @Override + public String escapePlainText(String plainTextContent) { + return _StringUtil.XMLEnc(plainTextContent); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) { + return builtInName.equals("xml"); + } + + @Override + protected TemplateXMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateXMLOutputModel(plainTextContent, markupContent); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/package.html b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/package.html new file mode 100644 index 0000000..6cb5c21 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/package.html @@ -0,0 +1,26 @@ +<!-- + 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. +--> +<html> +<head> +</head> +<body> +<p>Template output format: Standard implementations. This package is part of the +published API, that is, user code can safely depend on it.</p> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/package.html b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/package.html new file mode 100644 index 0000000..b25de83 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/package.html @@ -0,0 +1,25 @@ +<!-- + 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. +--> +<html> +<head> +</head> +<body> +<p>Template output format: Base classes/interfaces</p> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/package.html b/freemarker-core/src/main/java/org/apache/freemarker/core/package.html new file mode 100644 index 0000000..be9dab9 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/package.html @@ -0,0 +1,27 @@ +<!-- + 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. +--> +<html> +<head> +</head> +<body> +<p><b>The most commonly used API-s of FreeMarker;</b> +start with {@link freemarker.template.Configuration Configuration} (see also the +<a href="http://freemarker.org/docs/pgui_quickstart.html" target="_blank">Getting Stared</a> in the Manual.)</p> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/AndMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/AndMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/AndMatcher.java new file mode 100644 index 0000000..27b4156 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/AndMatcher.java @@ -0,0 +1,45 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +/** + * Logical "and" operation among the given matchers. + * + * @since 2.3.24 + */ +public class AndMatcher extends TemplateSourceMatcher { + + private final TemplateSourceMatcher[] matchers; + + public AndMatcher(TemplateSourceMatcher... matchers) { + if (matchers.length == 0) throw new IllegalArgumentException("Need at least 1 matcher, had 0."); + this.matchers = matchers; + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + for (TemplateSourceMatcher matcher : matchers) { + if (!(matcher.matches(sourceName, templateSource))) return false; + } + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorage.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorage.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorage.java new file mode 100644 index 0000000..c70fa94 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorage.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.templateresolver; + +import org.apache.freemarker.core.Configuration; + +/** + * Cache storage abstracts away the storage aspects of a cache - associating + * an object with a key, retrieval and removal via the key. It is actually a + * small subset of the {@link java.util.Map} interface. + * The implementations must be thread safe. + * + * @see Configuration#getCacheStorage() + */ +public interface CacheStorage { + Object get(Object key); + void put(Object key, Object value); + void remove(Object key); + void clear(); +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorageWithGetSize.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorageWithGetSize.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorageWithGetSize.java new file mode 100644 index 0000000..945d049 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/CacheStorageWithGetSize.java @@ -0,0 +1,36 @@ +/* + * 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.templateresolver; + +/** + * A cache storage that has a {@code getSize()} method for returning the current number of cache entries. + * + * @since 2.3.21 + */ +public interface CacheStorageWithGetSize extends CacheStorage { + + /** + * Returns the current number of cache entries. This is intended to be used for monitoring. Note that depending on + * the implementation, the cost of this operation is not necessary trivial, although calling it a few times per + * minute should not be a problem. + */ + int getSize(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/ConditionalTemplateConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/ConditionalTemplateConfigurationFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/ConditionalTemplateConfigurationFactory.java new file mode 100644 index 0000000..8fab61f --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/ConditionalTemplateConfigurationFactory.java @@ -0,0 +1,65 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +import org.apache.freemarker.core.TemplateConfiguration; + +/** + * Returns the given {@link TemplateConfiguration} directly, or another {@link TemplateConfigurationFactory}'s result, when + * the specified matcher matches the template source. + * + * @since 2.3.24 + */ +public class ConditionalTemplateConfigurationFactory extends TemplateConfigurationFactory { + + private final TemplateSourceMatcher matcher; + private final TemplateConfiguration templateConfiguration; + private final TemplateConfigurationFactory templateConfigurationFactory; + + public ConditionalTemplateConfigurationFactory( + TemplateSourceMatcher matcher, TemplateConfigurationFactory templateConfigurationFactory) { + this.matcher = matcher; + templateConfiguration = null; + this.templateConfigurationFactory = templateConfigurationFactory; + } + + public ConditionalTemplateConfigurationFactory( + TemplateSourceMatcher matcher, TemplateConfiguration templateConfiguration) { + this.matcher = matcher; + this.templateConfiguration = templateConfiguration; + templateConfigurationFactory = null; + } + + @Override + public TemplateConfiguration get(String sourceName, TemplateLoadingSource templateLoadingSource) + throws IOException, TemplateConfigurationFactoryException { + if (matcher.matches(sourceName, templateLoadingSource)) { + if (templateConfigurationFactory != null) { + return templateConfigurationFactory.get(sourceName, templateLoadingSource); + } else { + return templateConfiguration; + } + } else { + return null; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileExtensionMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileExtensionMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileExtensionMatcher.java new file mode 100644 index 0000000..c89a478 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileExtensionMatcher.java @@ -0,0 +1,85 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +/** + * Matches the file extension; unlike other matchers, by default case <em>insensitive</em>. A name (a path) is + * considered to have the given extension exactly if it ends with a dot plus the extension. + * + * @since 2.3.24 + */ +public class FileExtensionMatcher extends TemplateSourceMatcher { + + private final String extension; + private boolean caseInsensitive = true; + + /** + * @param extension + * The file extension (without the initial dot). Can't contain there characters: + * {@code '/'}, {@code '*'}, {@code '?'}. May contains {@code '.'}, but can't start with it. + */ + public FileExtensionMatcher(String extension) { + if (extension.indexOf('/') != -1) { + throw new IllegalArgumentException("A file extension can't contain \"/\": " + extension); + } + if (extension.indexOf('*') != -1) { + throw new IllegalArgumentException("A file extension can't contain \"*\": " + extension); + } + if (extension.indexOf('?') != -1) { + throw new IllegalArgumentException("A file extension can't contain \"*\": " + extension); + } + if (extension.startsWith(".")) { + throw new IllegalArgumentException("A file extension can't start with \".\": " + extension); + } + this.extension = extension; + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + int ln = sourceName.length(); + int extLn = extension.length(); + if (ln < extLn + 1 || sourceName.charAt(ln - extLn - 1) != '.') { + return false; + } + + return sourceName.regionMatches(caseInsensitive, ln - extLn, extension, 0, extLn); + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + /** + * Sets if the matching will be case insensitive (UNICODE compliant); default is {@code true}. + */ + public void setCaseInsensitive(boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + } + + /** + * Fluid API variation of {@link #setCaseInsensitive(boolean)} + */ + public FileExtensionMatcher caseInsensitive(boolean caseInsensitive) { + setCaseInsensitive(caseInsensitive); + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileNameGlobMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileNameGlobMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileNameGlobMatcher.java new file mode 100644 index 0000000..7a9692a --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FileNameGlobMatcher.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.templateresolver; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.apache.freemarker.core.util._StringUtil; + +/** + * As opposed to {@link PathGlobMatcher}, it only compares the "file name" part (the part after the last {@code /}) of + * the source name with the given glob. For example, the file name glob {@code *.ftlh} matches both {@code foo.ftlh} and + * {@code foo/bar.ftlh}. With other words, that file name glob is equivalent with the {@code **}{@code /*.ftlh}) + * <em>path</em> glob ( {@link PathGlobMatcher}). + * + * @since 2.3.24 + */ +public class FileNameGlobMatcher extends TemplateSourceMatcher { + + private final String glob; + + private Pattern pattern; + private boolean caseInsensitive; + + /** + * @param glob + * Glob with the syntax defined by {@link _StringUtil#globToRegularExpression(String, boolean)}. Must not + * start with {@code /}. + */ + public FileNameGlobMatcher(String glob) { + if (glob.indexOf('/') != -1) { + throw new IllegalArgumentException("A file name glob can't contain \"/\": " + glob); + } + this.glob = glob; + buildPattern(); + } + + private void buildPattern() { + pattern = _StringUtil.globToRegularExpression("**/" + glob, caseInsensitive); + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + return pattern.matcher(sourceName).matches(); + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + /** + * Sets if the matching will be case insensitive (UNICODE compliant); default is {@code false}. + */ + public void setCaseInsensitive(boolean caseInsensitive) { + boolean lastCaseInsensitive = this.caseInsensitive; + this.caseInsensitive = caseInsensitive; + if (lastCaseInsensitive != caseInsensitive) { + buildPattern(); + } + } + + /** + * Fluid API variation of {@link #setCaseInsensitive(boolean)} + */ + public FileNameGlobMatcher caseInsensitive(boolean caseInsensitive) { + setCaseInsensitive(caseInsensitive); + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FirstMatchTemplateConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FirstMatchTemplateConfigurationFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FirstMatchTemplateConfigurationFactory.java new file mode 100644 index 0000000..0f09d3d --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/FirstMatchTemplateConfigurationFactory.java @@ -0,0 +1,110 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +import org.apache.freemarker.core.TemplateConfiguration; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Returns the first non-{@code null} result of the child factories, ignoring all further child factories. The child + * factories are called in the order as they were added. + */ +public class FirstMatchTemplateConfigurationFactory extends TemplateConfigurationFactory { + + private final TemplateConfigurationFactory[] templateConfigurationFactories; + private boolean allowNoMatch; + private String noMatchErrorDetails; + + public FirstMatchTemplateConfigurationFactory(TemplateConfigurationFactory... templateConfigurationFactories) { + this.templateConfigurationFactories = templateConfigurationFactories; + } + + @Override + public TemplateConfiguration get(String sourceName, TemplateLoadingSource templateLoadingSource) + throws IOException, TemplateConfigurationFactoryException { + for (TemplateConfigurationFactory tcf : templateConfigurationFactories) { + TemplateConfiguration tc = tcf.get(sourceName, templateLoadingSource); + if (tc != null) { + return tc; + } + } + if (!allowNoMatch) { + throw new TemplateConfigurationFactoryException( + FirstMatchTemplateConfigurationFactory.class.getSimpleName() + + " has found no matching choice for source name " + + _StringUtil.jQuote(sourceName) + ". " + + (noMatchErrorDetails != null + ? "Error details: " + noMatchErrorDetails + : "(Set the noMatchErrorDetails property of the factory bean to give a more specific error " + + "message. Set allowNoMatch to true if this shouldn't be an error.)")); + } + return null; + } + + /** + * Getter pair of {@link #setAllowNoMatch(boolean)}. + */ + public boolean getAllowNoMatch() { + return allowNoMatch; + } + + /** + * Use this to specify if having no matching choice is an error. The default is {@code false}, that is, it's an + * error if there was no matching choice. + * + * @see #setNoMatchErrorDetails(String) + */ + public void setAllowNoMatch(boolean allowNoMatch) { + this.allowNoMatch = allowNoMatch; + } + + /** + * Use this to specify the text added to the exception error message when there was no matching choice. + * The default is {@code null} (no error details). + * + * @see #setAllowNoMatch(boolean) + */ + public String getNoMatchErrorDetails() { + return noMatchErrorDetails; + } + + + public void setNoMatchErrorDetails(String noMatchErrorDetails) { + this.noMatchErrorDetails = noMatchErrorDetails; + } + + /** + * Same as {@link #setAllowNoMatch(boolean)}, but return this object to support "fluent API" style. + */ + public FirstMatchTemplateConfigurationFactory allowNoMatch(boolean allow) { + setAllowNoMatch(allow); + return this; + } + + /** + * Same as {@link #setNoMatchErrorDetails(String)}, but return this object to support "fluent API" style. + */ + public FirstMatchTemplateConfigurationFactory noMatchErrorDetails(String message) { + setNoMatchErrorDetails(message); + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/GetTemplateResult.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/GetTemplateResult.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/GetTemplateResult.java new file mode 100644 index 0000000..58c9ea9 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/GetTemplateResult.java @@ -0,0 +1,89 @@ +/* + * 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.templateresolver; + +import java.io.Serializable; +import java.util.Locale; + +import org.apache.freemarker.core.Template; + +/** + * Used for the return value of {@link TemplateResolver#getTemplate(String, Locale, Serializable)} . + * + * @since 3.0.0 + */ +//TODO DRAFT only [FM3] +public final class GetTemplateResult { + + private final Template template; + private final String missingTemplateNormalizedName; + private final String missingTemplateReason; + private final Exception missingTemplateCauseException; + + public GetTemplateResult(Template template) { + this.template = template; + missingTemplateNormalizedName = null; + missingTemplateReason = null; + missingTemplateCauseException = null; + } + + public GetTemplateResult(String normalizedName, Exception missingTemplateCauseException) { + template = null; + missingTemplateNormalizedName = normalizedName; + missingTemplateReason = null; + this.missingTemplateCauseException = missingTemplateCauseException; + } + + public GetTemplateResult(String normalizedName, String missingTemplateReason) { + template = null; + missingTemplateNormalizedName = normalizedName; + this.missingTemplateReason = missingTemplateReason; + missingTemplateCauseException = null; + } + + /** + * The {@link Template} if it wasn't missing, otherwise {@code null}. + */ + public Template getTemplate() { + return template; + } + + /** + * When the template was missing, this <em>possibly</em> contains the explanation, or {@code null}. If the + * template wasn't missing (i.e., when {@link #getTemplate()} return non-{@code null}) this is always + * {@code null}. + */ + public String getMissingTemplateReason() { + return missingTemplateReason != null + ? missingTemplateReason + : (missingTemplateCauseException != null + ? missingTemplateCauseException.getMessage() + : null); + } + + /** + * When the template was missing, this <em>possibly</em> contains its normalized name. If the template wasn't + * missing (i.e., when {@link #getTemplate()} return non-{@code null}) this is always {@code null}. When the + * template is missing, it will be {@code null} for example if the normalization itself was unsuccessful. + */ + public String getMissingTemplateNormalizedName() { + return missingTemplateNormalizedName; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java new file mode 100644 index 0000000..cf19e93 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.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.templateresolver; + +import java.io.IOException; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.TemplateNotFoundException; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Indicates that the template name given was malformed according the {@link TemplateNameFormat} in use. Note that for + * backward compatibility, {@link DefaultTemplateNameFormatFM2} doesn't throw this exception, + * {@link DefaultTemplateNameFormat} does. This exception extends {@link IOException} for backward compatibility. + * + * @since 2.3.22 + * + * @see TemplateNotFoundException + * @see Configuration#getTemplate(String) + */ +@SuppressWarnings("serial") +public class MalformedTemplateNameException extends IOException { + + private final String templateName; + private final String malformednessDescription; + + public MalformedTemplateNameException(String templateName, String malformednessDescription) { + super("Malformed template name, " + _StringUtil.jQuote(templateName) + ": " + malformednessDescription); + this.templateName = templateName; + this.malformednessDescription = malformednessDescription; + } + + public String getTemplateName() { + return templateName; + } + + public String getMalformednessDescription() { + return malformednessDescription; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MergingTemplateConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MergingTemplateConfigurationFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MergingTemplateConfigurationFactory.java new file mode 100644 index 0000000..9b3106f --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/MergingTemplateConfigurationFactory.java @@ -0,0 +1,63 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +import org.apache.freemarker.core.TemplateConfiguration; + +/** + * Returns the merged results of all the child factories. The factories are merged in the order as they were added. + * {@code null} results from the child factories will be ignored. If all child factories return {@code null}, the result + * of this factory will be {@code null} too. + * + * @since 2.3.24 + */ +public class MergingTemplateConfigurationFactory extends TemplateConfigurationFactory { + + private final TemplateConfigurationFactory[] templateConfigurationFactories; + + public MergingTemplateConfigurationFactory(TemplateConfigurationFactory... templateConfigurationFactories) { + this.templateConfigurationFactories = templateConfigurationFactories; + } + + @Override + public TemplateConfiguration get(String sourceName, TemplateLoadingSource templateLoadingSource) + throws IOException, TemplateConfigurationFactoryException { + TemplateConfiguration.Builder mergedTCBuilder = null; + TemplateConfiguration firstResultTC = null; + for (TemplateConfigurationFactory tcf : templateConfigurationFactories) { + TemplateConfiguration tc = tcf.get(sourceName, templateLoadingSource); + if (tc != null) { + if (firstResultTC == null) { + firstResultTC = tc; + } else { + if (mergedTCBuilder == null) { + mergedTCBuilder = new TemplateConfiguration.Builder(); + mergedTCBuilder.merge(firstResultTC); + } + mergedTCBuilder.merge(tc); + } + } + } + + return mergedTCBuilder == null ? firstResultTC /* Maybe null */ : mergedTCBuilder.build(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/NotMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/NotMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/NotMatcher.java new file mode 100644 index 0000000..d608282 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/NotMatcher.java @@ -0,0 +1,41 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +/** + * Logical "not" operation on the given matcher. + * + * @since 2.3.24 + */ +public class NotMatcher extends TemplateSourceMatcher { + + private final TemplateSourceMatcher matcher; + + public NotMatcher(TemplateSourceMatcher matcher) { + this.matcher = matcher; + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + return !matcher.matches(sourceName, templateSource); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/OrMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/OrMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/OrMatcher.java new file mode 100644 index 0000000..922f293 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/OrMatcher.java @@ -0,0 +1,45 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +/** + * Logical "or" operation among the given matchers. + * + * @since 2.3.24 + */ +public class OrMatcher extends TemplateSourceMatcher { + + private final TemplateSourceMatcher[] matchers; + + public OrMatcher(TemplateSourceMatcher... matchers) { + if (matchers.length == 0) throw new IllegalArgumentException("Need at least 1 matcher, had 0."); + this.matchers = matchers; + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + for (TemplateSourceMatcher matcher : matchers) { + if ((matcher.matches(sourceName, templateSource))) return true; + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathGlobMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathGlobMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathGlobMatcher.java new file mode 100644 index 0000000..fa4213a --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathGlobMatcher.java @@ -0,0 +1,100 @@ +/* + * 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.templateresolver; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.apache.freemarker.core.util._StringUtil; + +/** + * Matches the whole template source name (also known as template source path) with the given glob. + * Note that the template source name is relative to the template storage root defined by the {@link TemplateLoader}; + * it's not the full path of a file on the file system. + * + * <p>This glob implementation recognizes {@code **} (Ant-style directory wildcard) among others. For more details see + * {@link _StringUtil#globToRegularExpression(String, boolean)}. + * + * <p>About the usage of {@code /} (slash): + * <ul> + * <li>You aren't allowed to start the glob with {@code /}, because template names (template paths) never start with + * it. + * <li>Future FreeMarker versions (compared to 2.3.24) might will support importing whole directories. Directory paths + * in FreeMarker should end with {@code /}. Hence, {@code foo/bar} refers to the file {bar}, while + * {@code foo/bar/} refers to the {bar} directory. + * </ul> + * + * <p>By default the glob is case sensitive, but this can be changed with {@link #setCaseInsensitive(boolean)} (or + * {@link #caseInsensitive(boolean)}). + * + * @since 2.3.24 + */ +public class PathGlobMatcher extends TemplateSourceMatcher { + + private final String glob; + + private Pattern pattern; + private boolean caseInsensitive; + + /** + * @param glob + * Glob with the syntax defined by {@link _StringUtil#globToRegularExpression(String, boolean)}. Must not + * start with {@code /}. + */ + public PathGlobMatcher(String glob) { + if (glob.startsWith("/")) { + throw new IllegalArgumentException("Absolute template paths need no inital \"/\"; remove it from: " + glob); + } + this.glob = glob; + buildPattern(); + } + + private void buildPattern() { + pattern = _StringUtil.globToRegularExpression(glob, caseInsensitive); + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + return pattern.matcher(sourceName).matches(); + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + /** + * Sets if the matching will be case insensitive (UNICODE compliant); default is {@code false}. + */ + public void setCaseInsensitive(boolean caseInsensitive) { + boolean lastCaseInsensitive = this.caseInsensitive; + this.caseInsensitive = caseInsensitive; + if (lastCaseInsensitive != caseInsensitive) { + buildPattern(); + } + } + + /** + * Fluid API variation of {@link #setCaseInsensitive(boolean)} + */ + public PathGlobMatcher caseInsensitive(boolean caseInsensitive) { + setCaseInsensitive(caseInsensitive); + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathRegexMatcher.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathRegexMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathRegexMatcher.java new file mode 100644 index 0000000..d015b1e --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/PathRegexMatcher.java @@ -0,0 +1,54 @@ +/* + * 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.templateresolver; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.apache.freemarker.core.util._StringUtil; + +/** + * Matches the whole template source name (also known as template source path) with the given regular expression. + * Note that the template source name is relative to the template storage root defined by the {@link TemplateLoader}; + * it's not the full path of a file on the file system. + * + * @since 2.3.24 + */ +public class PathRegexMatcher extends TemplateSourceMatcher { + + private final Pattern pattern; + + /** + * @param regex + * Glob with the syntax defined by {@link _StringUtil#globToRegularExpression(String)}. Must not + * start with {@code /}. + */ + public PathRegexMatcher(String regex) { + if (regex.startsWith("/")) { + throw new IllegalArgumentException("Absolute template paths need no inital \"/\"; remove it from: " + regex); + } + pattern = Pattern.compile(regex); + } + + @Override + public boolean matches(String sourceName, Object templateSource) throws IOException { + return pattern.matcher(sourceName).matches(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java new file mode 100644 index 0000000..fe9255d --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java @@ -0,0 +1,54 @@ +/* + * 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.templateresolver; + +import java.io.IOException; + +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateConfiguration; + +/** + * Creates (or returns) {@link TemplateConfiguration}-s for template sources. + * + * @since 2.3.24 + */ +public abstract class TemplateConfigurationFactory { + + /** + * Returns (maybe creates) the {@link TemplateConfiguration} for the given template source. + * + * @param sourceName + * The name (path) that was used for {@link TemplateLoader#load}. See + * {@link Template#getSourceName()} for details. + * @param templateLoadingSource + * The object returned by {@link TemplateLoadingResult#getSource()}. + * + * @return The {@link TemplateConfiguration} to apply, or {@code null} if the there's no {@link TemplateConfiguration} for + * this template source. + * + * @throws IOException + * Typically, if there factory needs further I/O to find out more about the template source, but that + * fails. + * @throws TemplateConfigurationFactoryException + * If there's a problem that's specific to the factory logic. + */ + public abstract TemplateConfiguration get(String sourceName, TemplateLoadingSource templateLoadingSource) + throws IOException, TemplateConfigurationFactoryException; + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactoryException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactoryException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactoryException.java new file mode 100644 index 0000000..26c4c7e --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactoryException.java @@ -0,0 +1,36 @@ +/* + * 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.templateresolver; + +/** + * Non-I/O exception thrown by {@link TemplateConfigurationFactory}-s. + * + * @since 2.3.24 + */ +public class TemplateConfigurationFactoryException extends Exception { + + public TemplateConfigurationFactoryException(String message) { + super(message); + } + + public TemplateConfigurationFactoryException(String message, Throwable cause) { + super(message, cause); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java new file mode 100644 index 0000000..fc6a4aa --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java @@ -0,0 +1,104 @@ +/* + * 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.templateresolver; + +import java.io.IOException; +import java.io.Serializable; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.TemplateNotFoundException; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; + +/** + * FreeMarker loads template "files" through objects that implement this interface, thus the templates need not be real + * files, and can come from any kind of data source (like classpath, servlet context, database, etc). While FreeMarker + * provides a few template loader implementations out-of-the-box, it's normal for embedding frameworks to use their own + * implementations. + * + * <p> + * The {@link TemplateLoader} used by FreeMaker is specified by the {@link Configuration#getTemplateLoader() + * templateLoader} configuration setting. + * + * <p> + * Implementations of this interface should be thread-safe. + * + * <p> + * Implementations should override {@link Object#toString()} to show information about from where the + * {@link TemplateLoader} loads the templates. For example, for a template loader that loads template from database + * table {@code toString} could return something like + * {@code "MyDatabaseTemplateLoader(user=\"cms\", table=\"mail_templates\")"}. This string will be shown in + * {@link TemplateNotFoundException} exception messages, next to the template name. + * + * <p> + * For those who has to dig deeper, note that the {@link TemplateLoader} is actually stored inside the + * {@link DefaultTemplateResolver} of the {@link Configuration}, and is normally only accessed directly by the + * {@link DefaultTemplateResolver}, and templates are get via the {@link DefaultTemplateResolver} API-s. + */ +public interface TemplateLoader { + + /** + * Creates a new session, or returns {@code null} if the template loader implementation doesn't support sessions. + * See {@link TemplateLoaderSession} for more information about sessions. + */ + TemplateLoaderSession createSession(); + + /** + * Loads the template content together with meta-data such as the version (usually the last modification time). + * + * @param name + * The name (template root directory relative path) of the template, already localized and normalized by + * the {@link org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver cache}. It is completely up to the loader implementation to + * interpret the name, however it should expect to receive hierarchical paths where path components are + * separated by a slash (not backslash). Backslashes (or any other OS specific separator character) are + * not considered as separators by FreeMarker, and thus they will not be replaced with slash before + * passing to this method, so it's up to the template loader to handle them (say, by throwing an + * exception that tells the user that the path (s)he has entered is invalid, as (s)he must use slash -- + * typical mistake of Windows users). The passed names are always considered relative to some + * loader-defined root location (often referred as the "template root directory"), and will never start + * with a slash, nor will they contain a path component consisting of either a single or a double dot -- + * these are all resolved by the template cache before passing the name to the loader. As a side effect, + * paths that trivially reach outside template root directory, such as <tt>../my.ftl</tt>, will be + * rejected by the template cache, so they never reach the template loader. Note again, that if the path + * uses backslash as path separator instead of slash as (the template loader should not accept that), the + * normalization will not properly happen, as FreeMarker (the cache) recognizes only the slashes as + * separators. + * @param ifSourceDiffersFrom + * If we only want to load the template if its source differs from this. {@code null} if you want the + * template to be loaded unconditionally. If this is {@code null} then the + * {@code ifVersionDiffersFrom} parameter must be {@code null} too. See + * {@link TemplateLoadingResult#getSource()} for more about sources. + * @param ifVersionDiffersFrom + * If we only want to load the template if its version (which is usually the last modification time) + * differs from this. {@code null} if {@code ifSourceDiffersFrom} is {@code null}, or if the backing + * storage from which the {@code ifSourceDiffersFrom} template source comes from doesn't store a version. + * See {@link TemplateLoadingResult#getVersion()} for more about versions. + * + * @return Not {@code null}. + */ + TemplateLoadingResult load(String name, TemplateLoadingSource ifSourceDiffersFrom, Serializable ifVersionDiffersFrom, + TemplateLoaderSession session) throws IOException; + + /** + * Invoked by {@link Configuration#clearTemplateCache()} to instruct this template loader to throw away its current + * state (some kind of cache usually) and start afresh. For most {@link TemplateLoader} implementations this does + * nothing. + */ + void resetState(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java new file mode 100644 index 0000000..6bf1b1f --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java @@ -0,0 +1,76 @@ +/* + * 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.templateresolver; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Serializable; + +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; + +/** + * Stores shared state between {@link TemplateLoader} operations that are executed close to each other in the same + * thread. For example, a {@link TemplateLoader} that reads from a database might wants to store the database + * connection in it for reuse. The goal of sessions is mostly to increase performance. However, because a + * {@link DefaultTemplateResolver#getTemplate(String, java.util.Locale, Serializable)} call is executed inside a single + * session, sessions can be also be utilized to ensure that the template lookup (see {@link TemplateLookupStrategy}) + * happens on a consistent view (a snapshot) of the backing storage, if the backing storage mechanism supports such + * thing. + * + * <p> + * The {@link TemplateLoaderSession} implementation is (usually) specific to the {@link TemplateLoader} + * implementation. If your {@link TemplateLoader} implementation can't take advantage of sessions, you don't have to + * implement this interface, just return {@code null} for {@link TemplateLoader#createSession()}. + * + * <p> + * {@link TemplateLoaderSession}-s should be lazy, that is, creating an instance should be very fast and should not + * cause I/O. Only when (and if ever) the shared resource stored in the session is needed for the first time should the + * shared resource be initialized. + * + * <p> + * {@link TemplateLoaderSession}-s need not be thread safe. + */ +public interface TemplateLoaderSession { + + /** + * Closes this session, freeing any resources it holds. Further operations involving this session should fail, with + * the exception of {@link #close()} itself, which should be silently ignored. + * + * <p> + * The {@link Reader} or {@link InputStream} contained in the {@link TemplateLoadingResult} must be closed before + * {@link #close()} is called on the session in which the {@link TemplateLoadingResult} was created. Except, if + * closing the {@link Reader} or {@link InputStream} has thrown an exception, the caller should just proceed with + * closing the session regardless. After {@link #close()} was called on the session, the methods of the + * {@link Reader} or {@link InputStream} is allowed to throw an exception, or behave in any other erratic way. + * (Because the caller of this interface is usually FreeMarker (the {@link DefaultTemplateResolver}), normally you don't have + * to deal with these rules, but it's useful to know the expectations if you implement + * {@link TemplateLoaderSession}.) + * + * <p>The caller of {@link TemplateLoader#createSession()} has to guarantee that {@link #close()} will be called on + * the created session. + */ + void close() throws IOException; + + /** + * Tells if this session is closed. + */ + boolean isClosed(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java new file mode 100644 index 0000000..c685d93 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java @@ -0,0 +1,208 @@ +/* + * 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.templateresolver; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.Reader; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.Date; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.Configuration.ExtendableBuilder; +import org.apache.freemarker.core.TemplateConfiguration; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; +import org.apache.freemarker.core.util._NullArgumentException; + +/** + * Return value of {@link TemplateLoader#load(String, TemplateLoadingSource, Serializable, TemplateLoaderSession)} + */ +public final class TemplateLoadingResult { + private final TemplateLoadingResultStatus status; + private final TemplateLoadingSource source; + private final Serializable version; + private final Reader reader; + private final InputStream inputStream; + private final TemplateConfiguration templateConfiguration; + + public static final TemplateLoadingResult NOT_FOUND = new TemplateLoadingResult( + TemplateLoadingResultStatus.NOT_FOUND); + public static final TemplateLoadingResult NOT_MODIFIED = new TemplateLoadingResult( + TemplateLoadingResultStatus.NOT_MODIFIED); + + /** + * Creates an instance with status {@link TemplateLoadingResultStatus#OPENED}, for a storage mechanism that + * naturally returns the template content as sequence of {@code char}-s as opposed to a sequence of {@code byte}-s. + * This is the case for example when you store the template in a database in a varchar or CLOB. Do <em>not</em> use + * this constructor for stores that naturally return binary data instead (like files, class loader resources, + * BLOB-s, etc.), because using this constructor will disable FreeMarker's charset selection mechanism. + * + * @param source + * See {@link #getSource()} + * @param version + * See {@link #getVersion()} for the meaning of this. Can be {@code null}, but use that only if the + * backing storage mechanism doesn't know this information. + * @param reader + * Gives the content of the template. It will be read in few thousand character chunks by FreeMarker, so + * generally it need not be a {@link BufferedReader}. + * @param templateConfiguration + * Usually {@code null}, as usually the backing storage mechanism doesn't store such information; + * see {@link #getTemplateConfiguration()}. + */ + public TemplateLoadingResult(TemplateLoadingSource source, Serializable version, Reader reader, + TemplateConfiguration templateConfiguration) { + _NullArgumentException.check("templateSource", source); + _NullArgumentException.check("reader", reader); + status = TemplateLoadingResultStatus.OPENED; + this.source = source; + this.version = version; + this.reader = reader; + inputStream = null; + this.templateConfiguration = templateConfiguration; + } + + /** + * Creates an instance with status {@link TemplateLoadingResultStatus#OPENED}, for a storage mechanism that + * naturally returns the template content as sequence of {@code byte}-s as opposed to a sequence of {@code char}-s. + * This is the case for example when you store the template in a file, classpath resource, or BLOB. Do <em>not</em> + * use this constructor for stores that naturally return text instead (like database varchar and CLOB columns). + * + * @param source + * See {@link #getSource()} + * @param version + * See {@link #getVersion()} for the meaning of this. Can be {@code null}, but use that only if the + * backing storage mechanism doesn't know this information. + * @param inputStream + * Gives the content of the template. It will be read in few thousand byte chunks by FreeMarker, so + * generally it need not be a {@link BufferedInputStream}. + * @param templateConfiguration + * Usually {@code null}, as usually the backing storage mechanism doesn't store such information; see + * {@link #getTemplateConfiguration()}. The most probable application is supplying the charset (encoding) + * used by the {@link InputStream} (via + * {@link ExtendableBuilder#setSourceEncoding(Charset)}), but only do that if the storage + * mechanism really knows what the charset is. + */ + public TemplateLoadingResult(TemplateLoadingSource source, Serializable version, InputStream inputStream, + TemplateConfiguration templateConfiguration) { + _NullArgumentException.check("templateSource", source); + _NullArgumentException.check("inputStream", inputStream); + status = TemplateLoadingResultStatus.OPENED; + this.source = source; + this.version = version; + reader = null; + this.inputStream = inputStream; + this.templateConfiguration = templateConfiguration; + } + + /** + * Used internally for creating the singleton instances which has a state where all other fields are {@code null}. + */ + private TemplateLoadingResult(TemplateLoadingResultStatus status) { + this.status = status; + source = null; + version = null; + reader = null; + inputStream = null; + templateConfiguration = null; + } + + /** + * Returns non-{@code null} exactly if {@link #getStatus()} is {@link TemplateLoadingResultStatus#OPENED} and the + * backing store mechanism returns content as {@code byte}-s, as opposed to as {@code chars}-s. See also + * {@link #TemplateLoadingResult(TemplateLoadingSource, Serializable, InputStream, TemplateConfiguration)}. It's the + * responsibility of the caller (which is {@link DefaultTemplateResolver} usually) to {@code close()} the {@link InputStream}. + * The return value is always the same instance, no mater when and how many times this method is called. + * + * <p> + * The returned {@code InputStream} will be read in few kilobyte chunks by FreeMarker, so generally it need not + * be a {@link BufferedInputStream}. + * + * @return {@code null} or a {@code InputStream} to read the template content; see method description for more. + */ + public InputStream getInputStream() { + return inputStream; + } + + /** + * Tells what kind of result this is; see the documentation of {@link TemplateLoadingResultStatus}. + */ + public TemplateLoadingResultStatus getStatus() { + return status; + } + + /** + * Identifies the source on the level of the storage mechanism; stored in the cache together with the version + * ({@link #getVersion()}). When checking if a cache entry is up to date, the sources are compared, and only if they + * are equal are the versions compared. See more at {@link TemplateLoadingSource}. + */ + public TemplateLoadingSource getSource() { + return source; + } + + /** + * If the result status is {@link TemplateLoadingResultStatus#OPENED} and the backing storage stores such + * information, the version (usually the last modification time) of the loaded template, otherwise {@code null}. The + * version is some kind of value which changes when the template in the backing storage is updated. Usually, it's + * the last modification time (a {@link Date} or {@link Long}), though that can be problematic if the template can + * change twice within the granularity of the clock used by the storage. Thus some storages may use a revision + * number instead that's always increased when the template is updated, or the cryptographic hash of the template + * content as the version. Version objects are compared with each other with their {@link Object#equals(Object)} + * method, to see if a cache entry is outdated (though only when the source objects ({@link #getSource()}) are + * equal). Thus, the version object must have proper {@link Object#equals(Object)} and {@link Object#hashCode()} + * methods. + */ + public Serializable getVersion() { + return version; + } + + /** + * Returns non-{@code null} exactly if {@link #getStatus()} is {@link TemplateLoadingResultStatus#OPENED} and the + * backing store mechanism returns content as {@code char}-s, as opposed to as {@code byte}-s. See also + * {@link #TemplateLoadingResult(TemplateLoadingSource, Serializable, Reader, TemplateConfiguration)}. It's the + * responsibility of the caller (which is {@link DefaultTemplateResolver} usually) to {@code close()} the {@link Reader}. The + * return value is always the same instance, no mater when and how many times this method is called. + * + * <p> + * The returned {@code Reader} will be read in few thousand character chunks by FreeMarker, so generally it need not + * be a {@link BufferedReader}. + * + * @return {@code null} or a {@code Reader} to read the template content; see method description for more. + */ + public Reader getReader() { + return reader; + } + + /** + * If {@link #getStatus()} is {@link TemplateLoadingResultStatus#OPENED}, and the template loader stores such + * information (which is rare) then it returns the {@link TemplateConfiguration} applicable to the template, + * otherwise it returns {@code null}. If {@link #getStatus()} is not {@link TemplateLoadingResultStatus#OPENED}, + * then this should always return {@code null}. If there are {@link TemplateConfiguration}-s coming from other + * sources, such as from {@link Configuration#getTemplateConfigurations()}, this won't replace them, but will be + * merged with them, with properties coming from the returned {@link TemplateConfiguration} having the highest + * priority. + * + * @return {@code null}, or a {@link TemplateConfiguration}. + */ + public TemplateConfiguration getTemplateConfiguration() { + return templateConfiguration; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResultStatus.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResultStatus.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResultStatus.java new file mode 100644 index 0000000..0ac8d00 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResultStatus.java @@ -0,0 +1,49 @@ +/* + * 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.templateresolver; + +import java.io.Serializable; + +/** + * Used for the value of {@link TemplateLoadingResult#getStatus()}. + */ +public enum TemplateLoadingResultStatus { + + /** + * The template with the requested name doesn't exist (not to be confused with "wasn't accessible due to error"). If + * there was and error because of which we can't know for sure if the template is there or not (for example we + * couldn't access the backing store due to a network connection error or other unexpected I/O error or + * authorization problem), this value must not be used, instead an exception should be thrown by + * {@link TemplateLoader#load(String, TemplateLoadingSource, Serializable, TemplateLoaderSession)}. + */ + NOT_FOUND, + + /** + * If the template was found, but its source and version is the same as that which was provided to + * {@link TemplateLoader#load(String, TemplateLoadingSource, Serializable, TemplateLoaderSession)} (from a cache + * presumably), so its content wasn't opened for reading. + */ + NOT_MODIFIED, + + /** + * If the template was found and its content is ready for reading. + */ + OPENED + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java new file mode 100644 index 0000000..bfe47e4 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java @@ -0,0 +1,69 @@ +/* + * 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.templateresolver; + +import java.io.Serializable; +import java.util.HashMap; + +import org.apache.freemarker.core.templateresolver.impl.ByteArrayTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; + +/** + * The point of {@link TemplateLoadingSource} is that with their {@link Object#equals(Object)} method we can tell if two + * cache entries were generated from the same physical resource or not. Comparing the template names isn't enough, + * because a {@link TemplateLoader} may uses some kind of fallback mechanism, such as delegating to other + * {@link TemplateLoader}-s until the template is found. Like if we have two {@link FileTemplateLoader}-s with different + * physical root directories, both can contain {@code "foo/bar.ftl"}, but obviously the two files aren't the same. + * + * <p> + * When implementing this interface, check these: + * + * <ul> + * <li>{@link Object#equals(Object)} must not be based on object identity, because two instances of + * {@link TemplateLoadingSource} that describe the same resource must be equivalent. + * + * <li>Each {@link TemplateLoader} implementation should have its own {@link TemplateLoadingSource} implementation, so + * that {@link TemplateLoadingSource}-s coming from different {@link TemplateLoader} implementations can't be + * accidentally equal (according to {@link Object#equals(Object)}). + * + * <li>{@link Object#equals(Object)} must still work properly if there are multiple instances of the same + * {@link TemplateLoader} implementation. Like if you have an implementation that loads from a database table, the + * {@link TemplateLoadingSource} should certain contain the JDBC connection string, the table name and the row ID, not + * just the row ID. + * + * <li>Together with {@link Object#equals(Object)}, {@link Object#hashCode()} must be also overridden. The template + * source may be used as a {@link HashMap} key. + * + * <li>Because {@link TemplateLoadingSource}-s are {@link Serializable}, they can't contain non-{@link Serializable} + * fields. Most notably, a reference to the creator {@link TemplateLoader}, so if it's an inner class of the + * {@link TemplateLoader}, it should be static. + * + * <li>Consider that cache entries, in which the source is stored, may move between JVM-s (because of clustering with a + * distributed cache). Thus they should remain meaningful for the purpose of {@link Object#equals(Object)} even in + * another JVM. + * + * <li>A {@link TemplateLoader} may chose not to support distributed caches, like {@link ByteArrayTemplateLoader} + * doesn't support that for example. In that case the serialization related points above can be ignored, but the + * {@link TemplateLoadingSource} implementation should define the {@code writeObject} method (a Java serialization + * feature) and throw an exception there to block serialization attempts. + * </ul> + */ +public interface TemplateLoadingSource extends Serializable { + // Empty +}
