Repository: incubator-freemarker Updated Branches: refs/heads/3 2f1f291dd -> 8915ac9bd
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java index 574fa82..356e6ed 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java @@ -19,6 +19,8 @@ package org.apache.freemarker.core; +import static org.apache.freemarker.core.Configuration.ExtendableBuilder.*; + import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -76,7 +78,6 @@ import org.apache.freemarker.core.templateresolver.impl.MruCacheStorage; import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util.CaptureOutput; -import org.apache.freemarker.core.util.CommonBuilder; import org.apache.freemarker.core.util.HtmlEscape; import org.apache.freemarker.core.util.NormalizeNewlines; import org.apache.freemarker.core.util.StandardCompress; @@ -137,8 +138,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; * <p>The setting reader methods of this class don't throw {@link CoreSettingValueNotSetException}, because all settings * are set on the {@link Configuration} level (even if they were just initialized to a default value). */ -public final class Configuration - implements TopLevelConfiguration, CustomStateScope { +public final class Configuration implements TopLevelConfiguration, CustomStateScope { private static final String VERSION_PROPERTIES_PATH = "org/apache/freemarker/core/version.properties"; @@ -237,8 +237,14 @@ public final class Configuration // Configuration-specific settings: private final Version incompatibleImprovements; - private final DefaultTemplateResolver templateResolver; - private final boolean localizedLookup; + private final TemplateResolver templateResolver; + private final TemplateLoader templateLoader; + private final CacheStorage cacheStorage; + private final TemplateLookupStrategy templateLookupStrategy; + private final TemplateNameFormat templateNameFormat; + private final TemplateConfigurationFactory templateConfigurations; + private final Long templateUpdateDelayMilliseconds; + private final Boolean localizedLookup; private final List<OutputFormat> registeredCustomOutputFormats; private final Map<String, OutputFormat> registeredCustomOutputFormatsByName; private final Map<String, Object> sharedVariables; @@ -291,20 +297,9 @@ public final class Configuration private <SelfT extends ExtendableBuilder<SelfT>> Configuration(ExtendableBuilder<SelfT> builder) throws ConfigurationException { - // Configuration-specific settings: - + // Configuration-specific settings (except templateResolver): incompatibleImprovements = builder.getIncompatibleImprovements(); - templateResolver = new DefaultTemplateResolver( - builder.getTemplateLoader(), - builder.getCacheStorage(), builder.getTemplateUpdateDelayMilliseconds(), - builder.getTemplateLookupStrategy(), builder.getLocalizedLookup(), - builder.getTemplateNameFormat(), - builder.getTemplateConfigurations(), - this); - - localizedLookup = builder.getLocalizedLookup(); - { Collection<OutputFormat> registeredCustomOutputFormats = builder.getRegisteredCustomOutputFormats(); @@ -438,6 +433,67 @@ public final class Configuration lazyImports = builder.getLazyImports(); lazyAutoImports = builder.getLazyAutoImports(); customSettings = builder.getCustomSettings(false); + + // Configuration-specific settings continued... templateResolver): + + templateResolver = builder.getTemplateResolver(); + + templateLoader = builder.getTemplateLoader(); + if (!templateResolver.supportsTemplateLoaderSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, TEMPLATE_LOADER_KEY, templateLoader); + } + + cacheStorage = builder.getCacheStorage(); + if (!templateResolver.supportsCacheStorageSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, CACHE_STORAGE_KEY, cacheStorage); + } + + templateUpdateDelayMilliseconds = builder.getTemplateUpdateDelayMilliseconds(); + if (!templateResolver.supportsTemplateUpdateDelayMillisecondsSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, TEMPLATE_UPDATE_DELAY_KEY, templateUpdateDelayMilliseconds); + } + + templateLookupStrategy = builder.getTemplateLookupStrategy(); + if (!templateResolver.supportsTemplateLookupStrategySetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, TEMPLATE_LOOKUP_STRATEGY_KEY, templateLookupStrategy); + } + + localizedLookup = builder.getLocalizedLookup(); + if (!templateResolver.supportsLocalizedLookupSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, LOCALIZED_LOOKUP_KEY, localizedLookup); + } + + templateNameFormat = builder.getTemplateNameFormat(); + if (!templateResolver.supportsTemplateNameFormatSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, TEMPLATE_NAME_FORMAT_KEY, templateNameFormat); + } + + templateConfigurations = builder.getTemplateConfigurations(); + if (!templateResolver.supportsTemplateConfigurationsSetting()) { + checkSettingIsNullForThisTemplateResolver( + templateResolver, TEMPLATE_CONFIGURATIONS_KEY, templateConfigurations); + } + + templateResolver.setDependencies(new TemplateResolverDependenciesImpl(this, templateResolver)); + } + + private void checkSettingIsNullForThisTemplateResolver( + TemplateResolver templateResolver, + String settingName, Object value) { + if (value != null) { + throw new ConfigurationSettingValueException( + settingName, null, false, + "The templateResolver is a " + + templateResolver.getClass().getName() + ", which doesn't support this setting, hence it " + + "mustn't be set or must be set to null.", + null); + } } private <SelfT extends ExtendableBuilder<SelfT>> void wrapAndPutSharedVariables( @@ -478,11 +534,24 @@ public final class Configuration } @Override + public TemplateResolver getTemplateResolver() { + return templateResolver; + } + + + + /** + * Always {@code true} in {@link Configuration}-s; even if this setting wasn't set in the builder, it gets a default + * value in the {@link Configuration}. + */ + @Override + public boolean isTemplateResolverSet() { + return true; + } + + @Override public TemplateLoader getTemplateLoader() { - if (templateResolver == null) { - return null; - } - return templateResolver.getTemplateLoader(); + return templateLoader; } /** @@ -496,10 +565,7 @@ public final class Configuration @Override public TemplateLookupStrategy getTemplateLookupStrategy() { - if (templateResolver == null) { - return null; - } - return templateResolver.getTemplateLookupStrategy(); + return templateLookupStrategy; } /** @@ -513,10 +579,7 @@ public final class Configuration @Override public TemplateNameFormat getTemplateNameFormat() { - if (templateResolver == null) { - return null; - } - return templateResolver.getTemplateNameFormat(); + return templateNameFormat; } /** @@ -530,10 +593,7 @@ public final class Configuration @Override public TemplateConfigurationFactory getTemplateConfigurations() { - if (templateResolver == null) { - return null; - } - return templateResolver.getTemplateConfigurations(); + return templateConfigurations; } /** @@ -547,7 +607,7 @@ public final class Configuration @Override public CacheStorage getCacheStorage() { - return templateResolver.getCacheStorage(); + return cacheStorage; } /** @@ -560,8 +620,8 @@ public final class Configuration } @Override - public long getTemplateUpdateDelayMilliseconds() { - return templateResolver.getTemplateUpdateDelayMilliseconds(); + public Long getTemplateUpdateDelayMilliseconds() { + return templateUpdateDelayMilliseconds; } /** @@ -1350,7 +1410,7 @@ public final class Configuration if (locale == null) { locale = getLocale(); } - final GetTemplateResult maybeTemp = templateResolver.getTemplate(name, locale, customLookupCondition); + final GetTemplateResult maybeTemp = getTemplateResolver().getTemplate(name, locale, customLookupCondition); final Template temp = maybeTemp.getTemplate(); if (temp == null) { if (ignoreMissing) { @@ -1455,7 +1515,7 @@ public final class Configuration * <p>This method is thread-safe and can be called while the engine processes templates. */ public void clearTemplateCache() { - templateResolver.clearTemplateCache(); + getTemplateResolver().clearTemplateCache(); } /** @@ -1472,12 +1532,12 @@ public final class Configuration */ public void removeTemplateFromCache(String name, Locale locale, Serializable customLookupCondition) throws IOException { - templateResolver.removeTemplateFromCache(name, locale, customLookupCondition); + getTemplateResolver().removeTemplateFromCache(name, locale, customLookupCondition); } @Override - public boolean getLocalizedLookup() { - return templateResolver.getLocalizedLookup(); + public Boolean getLocalizedLookup() { + return localizedLookup; } /** @@ -1600,7 +1660,7 @@ public final class Configuration */ public abstract static class ExtendableBuilder<SelfT extends ExtendableBuilder<SelfT>> extends MutableParsingAndProcessingConfiguration<SelfT> - implements TopLevelConfiguration, CommonBuilder<Configuration> { + implements TopLevelConfiguration, org.apache.freemarker.core.util.CommonBuilder<Configuration> { /** Legacy, snake case ({@code like_this}) variation of the setting name. */ public static final String SOURCE_ENCODING_KEY_SNAKE_CASE = "source_encoding"; @@ -1726,16 +1786,23 @@ public final class Configuration // Set early in the constructor to non-null private Version incompatibleImprovements = Configuration.VERSION_3_0_0; + private TemplateResolver templateResolver; + private TemplateResolver cachedDefaultTemplateResolver; private TemplateLoader templateLoader; private boolean templateLoaderSet; private CacheStorage cacheStorage; + private boolean cacheStorageSet; private CacheStorage cachedDefaultCacheStorage; private TemplateLookupStrategy templateLookupStrategy; + private boolean templateLookupStrategySet; private TemplateNameFormat templateNameFormat; + private boolean templateNameFormatSet; private TemplateConfigurationFactory templateConfigurations; private boolean templateConfigurationsSet; private Long templateUpdateDelayMilliseconds; + private boolean templateUpdateDelayMillisecondsSet; private Boolean localizedLookup; + private boolean localizedLookupSet; private Collection<OutputFormat> registeredCustomOutputFormats; private Map<String, Object> sharedVariables; @@ -2024,8 +2091,48 @@ public final class Configuration } @Override + public TemplateResolver getTemplateResolver() { + return isTemplateResolverSet() ? templateResolver : getDefaultTemplateResolver(); + } + + @Override + public boolean isTemplateResolverSet() { + return templateResolver != null; + } + + protected TemplateResolver getDefaultTemplateResolver() { + if (cachedDefaultTemplateResolver == null) { + cachedDefaultTemplateResolver = new DefaultTemplateResolver(); + } + return cachedDefaultTemplateResolver; + } + + /** + * Setter pair of {@link Configuration#getTemplateResolver()}; note {@code null}. + */ + public void setTemplateResolver(TemplateResolver templateResolver) { + _NullArgumentException.check("templateResolver", templateResolver); + this.templateResolver = templateResolver; + } + + /** + * Fluent API equivalent of {@link #setTemplateResolver(TemplateResolver)} + */ + public SelfT templateResolver(TemplateResolver templateResolver) { + setTemplateResolver(templateResolver); + return self(); + } + + /** + * Resets this setting to its initial state, as if it was never set. + */ + public void unsetTemplateResolver() { + templateResolver = null; + } + + @Override public TemplateLoader getTemplateLoader() { - return isTemplateLoaderSet() ? templateLoader : getDefaultTemplateLoader(); + return isTemplateLoaderSet() ? templateLoader : getDefaultTemplateLoaderTRAware(); } @Override @@ -2033,12 +2140,21 @@ public final class Configuration return templateLoaderSet; } + private TemplateLoader getDefaultTemplateLoaderTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsTemplateLoaderSetting() ? + null : getDefaultTemplateLoader(); + } + + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ protected TemplateLoader getDefaultTemplateLoader() { return null; } /** - * Setter pair of {@link Configuration#getTemplateLoader()}. + * Setter pair of {@link Configuration#getTemplateLoader()}. Note that {@code null} is a valid value. */ public void setTemplateLoader(TemplateLoader templateLoader) { this.templateLoader = templateLoader; @@ -2063,14 +2179,23 @@ public final class Configuration @Override public CacheStorage getCacheStorage() { - return isCacheStorageSet() ? cacheStorage : getDefaultCacheStorage(); + return isCacheStorageSet() ? cacheStorage : getDefaultCacheStorageTRAware(); } @Override public boolean isCacheStorageSet() { - return cacheStorage != null; + return cacheStorageSet; } + private CacheStorage getDefaultCacheStorageTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsCacheStorageSetting() ? null + : getDefaultCacheStorage(); + } + + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ protected CacheStorage getDefaultCacheStorage() { if (cachedDefaultCacheStorage == null) { // If this will depend on incompatibleImprovements, null it out in onIncompatibleImprovementsChanged()! @@ -2084,6 +2209,7 @@ public final class Configuration */ public void setCacheStorage(CacheStorage cacheStorage) { this.cacheStorage = cacheStorage; + this.cacheStorageSet = true; cachedDefaultCacheStorage = null; } @@ -2100,19 +2226,29 @@ public final class Configuration */ public void unsetCacheStorage() { cacheStorage = null; + cacheStorageSet = false; } @Override public TemplateLookupStrategy getTemplateLookupStrategy() { - return isTemplateLookupStrategySet() ? templateLookupStrategy : getDefaultTemplateLookupStrategySet(); + return isTemplateLookupStrategySet() ? templateLookupStrategy : getDefaultTemplateLookupStrategyTRAware(); } @Override public boolean isTemplateLookupStrategySet() { - return templateLookupStrategy != null; + return templateLookupStrategySet; + } + + private TemplateLookupStrategy getDefaultTemplateLookupStrategyTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsTemplateLookupStrategySetting() ? null : + getDefaultTemplateLookupStrategy(); } - protected TemplateLookupStrategy getDefaultTemplateLookupStrategySet() { + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ + protected TemplateLookupStrategy getDefaultTemplateLookupStrategy() { return DefaultTemplateLookupStrategy.INSTANCE; } @@ -2120,8 +2256,8 @@ public final class Configuration * Setter pair of {@link Configuration#getTemplateLookupStrategy()}. */ public void setTemplateLookupStrategy(TemplateLookupStrategy templateLookupStrategy) { - _NullArgumentException.check("templateLookupStrategy", templateLookupStrategy); this.templateLookupStrategy = templateLookupStrategy; + templateLookupStrategySet = true; } /** @@ -2137,11 +2273,12 @@ public final class Configuration */ public void unsetTemplateLookupStrategy() { templateLookupStrategy = null; + templateLookupStrategySet = false; } @Override public TemplateNameFormat getTemplateNameFormat() { - return isTemplateNameFormatSet() ? templateNameFormat : getDefaultTemplateNameFormat(); + return isTemplateNameFormatSet() ? templateNameFormat : getDefaultTemplateNameFormatTRAware(); } /** @@ -2149,9 +2286,18 @@ public final class Configuration */ @Override public boolean isTemplateNameFormatSet() { - return templateNameFormat != null; + return templateNameFormatSet; + } + + private TemplateNameFormat getDefaultTemplateNameFormatTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsTemplateNameFormatSetting() ? null + : getDefaultTemplateNameFormat(); } + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ protected TemplateNameFormat getDefaultTemplateNameFormat() { return DefaultTemplateNameFormatFM2.INSTANCE; } @@ -2160,8 +2306,8 @@ public final class Configuration * Setter pair of {@link Configuration#getTemplateNameFormat()}. */ public void setTemplateNameFormat(TemplateNameFormat templateNameFormat) { - _NullArgumentException.check("templateNameFormat", templateNameFormat); this.templateNameFormat = templateNameFormat; + templateNameFormatSet = true; } /** @@ -2177,11 +2323,12 @@ public final class Configuration */ public void unsetTemplateNameFormat() { this.templateNameFormat = null; + templateNameFormatSet = false; } @Override public TemplateConfigurationFactory getTemplateConfigurations() { - return isTemplateConfigurationsSet() ? templateConfigurations : getDefaultTemplateConfigurations(); + return isTemplateConfigurationsSet() ? templateConfigurations : getDefaultTemplateConfigurationsTRAware(); } @Override @@ -2189,6 +2336,15 @@ public final class Configuration return templateConfigurationsSet; } + private TemplateConfigurationFactory getDefaultTemplateConfigurationsTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsTemplateConfigurationsSetting() ? null + : getDefaultTemplateConfigurations(); + } + + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ protected TemplateConfigurationFactory getDefaultTemplateConfigurations() { return null; } @@ -2218,31 +2374,41 @@ public final class Configuration } @Override - public long getTemplateUpdateDelayMilliseconds() { + public Long getTemplateUpdateDelayMilliseconds() { return isTemplateUpdateDelayMillisecondsSet() ? templateUpdateDelayMilliseconds - : getDefaultTemplateUpdateDelayMilliseconds(); + : getDefaultTemplateUpdateDelayMillisecondsTRAware(); } @Override public boolean isTemplateUpdateDelayMillisecondsSet() { - return templateUpdateDelayMilliseconds != null; + return templateUpdateDelayMillisecondsSet; + } + + private Long getDefaultTemplateUpdateDelayMillisecondsTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsTemplateUpdateDelayMillisecondsSetting() + ? null : getDefaultTemplateUpdateDelayMilliseconds(); } - protected long getDefaultTemplateUpdateDelayMilliseconds() { - return 5000; + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ + protected Long getDefaultTemplateUpdateDelayMilliseconds() { + return 5000L; } /** * Setter pair of {@link Configuration#getTemplateUpdateDelayMilliseconds()}. */ - public void setTemplateUpdateDelayMilliseconds(long templateUpdateDelayMilliseconds) { + public void setTemplateUpdateDelayMilliseconds(Long templateUpdateDelayMilliseconds) { this.templateUpdateDelayMilliseconds = templateUpdateDelayMilliseconds; + templateUpdateDelayMillisecondsSet = true; } /** - * Fluent API equivalent of {@link #setTemplateUpdateDelayMilliseconds(long)} + * Fluent API equivalent of {@link #setTemplateUpdateDelayMilliseconds(Long)} */ - public SelfT templateUpdateDelayMilliseconds(long templateUpdateDelayMilliseconds) { + public SelfT templateUpdateDelayMilliseconds(Long templateUpdateDelayMilliseconds) { setTemplateUpdateDelayMilliseconds(templateUpdateDelayMilliseconds); return self(); } @@ -2252,19 +2418,29 @@ public final class Configuration */ public void unsetTemplateUpdateDelayMilliseconds() { templateUpdateDelayMilliseconds = null; + templateUpdateDelayMillisecondsSet = false; } @Override - public boolean getLocalizedLookup() { - return isLocalizedLookupSet() ? localizedLookup : getDefaultLocalizedLookup(); + public Boolean getLocalizedLookup() { + return isLocalizedLookupSet() ? localizedLookup : getDefaultLocalizedLookupTRAware(); } @Override public boolean isLocalizedLookupSet() { - return localizedLookup != null; + return localizedLookupSet; + } + + private Boolean getDefaultLocalizedLookupTRAware() { + return isTemplateResolverSet() && !getTemplateResolver().supportsLocalizedLookupSetting() ? null + : getDefaultLocalizedLookup(); } - protected boolean getDefaultLocalizedLookup() { + /** + * The default value when the {@link #getTemplateResolver() templateResolver} supports this setting (otherwise + * the default is hardwired to be {@code null} and this method isn't called). + */ + protected Boolean getDefaultLocalizedLookup() { return true; } @@ -2273,6 +2449,7 @@ public final class Configuration */ public void setLocalizedLookup(Boolean localizedLookup) { this.localizedLookup = localizedLookup; + localizedLookupSet = true; } /** @@ -2288,6 +2465,7 @@ public final class Configuration */ public void unsetLocalizedLookup() { this.localizedLookup = null; + localizedLookupSet = false; } public Collection<OutputFormat> getRegisteredCustomOutputFormats() { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/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 index 3ed6512..193ec96 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java @@ -57,7 +57,7 @@ public class ConfigurationSettingValueException extends ConfigurationException { Throwable cause) { super( createMessage( - name, value, true, + name, value, showValue, reason != null ? ", because: " : (cause != null ? "; see cause exception." : null), reason), cause); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java index b3c4150..316bd00 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java @@ -61,9 +61,7 @@ import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.TransformControl; import org.apache.freemarker.core.model.impl.SimpleHash; import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException; -import org.apache.freemarker.core.templateresolver.TemplateNameFormat; import org.apache.freemarker.core.templateresolver.TemplateResolver; -import org.apache.freemarker.core.templateresolver._CacheAPI; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2; import org.apache.freemarker.core.util.UndeclaredThrowableException; @@ -2778,8 +2776,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen // We can't cause a template lookup here (see TemplateLookupStrategy), as that can be expensive. We exploit // that (at least in 2.3.x) the name used for eager import namespace key isn't the template.sourceName, but // the looked up name (template.name), which we can get quickly: - TemplateNameFormat tnf = getConfiguration().getTemplateNameFormat(); - templateName = _CacheAPI.normalizeRootBasedName(tnf, templateName); + templateName = getConfiguration().getTemplateResolver().normalizeRootBasedName(templateName); } if (loadedLibs == null) { @@ -2857,19 +2854,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen if (baseName == null) { return targetName; } - return _CacheAPI.toRootBasedName(configuration.getTemplateNameFormat(), baseName, targetName); - } - - String renderElementToString(ASTElement te) throws IOException, TemplateException { - Writer prevOut = out; - try { - StringWriter sw = new StringWriter(); - out = sw; - visit(te); - return sw.toString(); - } finally { - out = prevOut; - } + return configuration.getTemplateResolver().toRootBasedName(baseName, targetName); } void importMacros(Template template) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java index 60a99ab..18e17f6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java @@ -195,9 +195,8 @@ public interface ParsingConfiguration { /** * Tells if the "file" extension part of the source name ({@link Template#getSourceName()}) will influence certain - * parsing settings. For backward compatibility, it defaults to {@code false} if - * {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting from {@code incompatibleImprovements} - * 2.3.24, it defaults to {@code true}, so the following standard file extensions take their effect: + * parsing settings. Defaults to {@code true}. When {@code true}, the following standard file extensions take + * their effect: * * <ul> * <li>{@code ftlh}: Sets the {@link #getOutputFormat() outputFormat} setting to {@code "HTML"} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java index 766669e..dc136e8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java @@ -25,10 +25,6 @@ package org.apache.freemarker.core; */ public abstract class SettingValueNotSetException extends RuntimeException { - public SettingValueNotSetException(String message) { - super(message); - } - public SettingValueNotSetException(String message, Throwable cause) { super(message, cause); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java new file mode 100644 index 0000000..10b219f --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java @@ -0,0 +1,120 @@ +/* + * 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 static org.apache.freemarker.core.Configuration.ExtendableBuilder.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.TemplateNameFormat; +import org.apache.freemarker.core.templateresolver.TemplateResolver; +import org.apache.freemarker.core.templateresolver.TemplateResolverDependencies; +import org.apache.freemarker.core.util._NullArgumentException; + +/** + * Used internally by {@link Configuration}. + */ +class TemplateResolverDependenciesImpl extends TemplateResolverDependencies { + + private final TemplateResolver templateResolver; + private final Configuration configuration; + + public TemplateResolverDependenciesImpl(Configuration configuration, TemplateResolver templateResolver) { + _NullArgumentException.check("configuration", configuration); + _NullArgumentException.check("templateResolver", templateResolver); + this.templateResolver = templateResolver; + this.configuration = configuration; + } + + @Override + public TemplateLoader getTemplateLoader() { + checkSettingSupported(TEMPLATE_LOADER_KEY, templateResolver.supportsTemplateLoaderSetting()); + return configuration.getTemplateLoader(); + } + + @Override + public CacheStorage getCacheStorage() { + checkSettingSupported(CACHE_STORAGE_KEY, templateResolver.supportsCacheStorageSetting()); + return configuration.getCacheStorage(); + } + + @Override + public TemplateLookupStrategy getTemplateLookupStrategy() { + checkSettingSupported(TEMPLATE_LOOKUP_STRATEGY_KEY, templateResolver.supportsTemplateLookupStrategySetting()); + return configuration.getTemplateLookupStrategy(); + } + + @Override + public TemplateNameFormat getTemplateNameFormat() { + checkSettingSupported(TEMPLATE_NAME_FORMAT_KEY, templateResolver.supportsTemplateNameFormatSetting()); + return configuration.getTemplateNameFormat(); + } + + @Override + public TemplateConfigurationFactory getTemplateConfigurations() { + checkSettingSupported(TEMPLATE_CONFIGURATIONS_KEY, templateResolver.supportsTemplateConfigurationsSetting()); + return configuration.getTemplateConfigurations(); + } + + @Override + public Long getTemplateUpdateDelayMilliseconds() { + checkSettingSupported( + TEMPLATE_UPDATE_DELAY_KEY, templateResolver.supportsTemplateUpdateDelayMillisecondsSetting()); + return configuration.getTemplateUpdateDelayMilliseconds(); + } + + @Override + public Boolean getLocalizedLookup() { + checkSettingSupported(LOCALIZED_LOOKUP_KEY, templateResolver.supportsLocalizedLookupSetting()); + return configuration.getLocalizedLookup(); + } + + @Override + public Charset getSourceEncoding() { + return configuration.getSourceEncoding(); + } + + @Override + public TemplateLanguage getTemplateLanguage() { + return configuration.getTemplateLanguage(); + } + + private void checkSettingSupported(String name, boolean supported) { + if (!supported) { + throw new IllegalStateException(templateResolver.getClass().getName() + " reported that it doesn't support " + + "this setting, so you aren't allowed to get it: " + name); + } + } + + @Override + public Template parse(TemplateLanguage templateLanguage, String name, String sourceName, Reader reader, + TemplateConfiguration templateConfiguration, Charset encoding, InputStream streamToUnmarkWhenEncEstabd) + throws IOException, ParseException { + return templateLanguage.parse(name, sourceName, reader, + configuration, templateConfiguration, encoding, streamToUnmarkWhenEncEstabd); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java index 5ad4490..18e705b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java @@ -27,10 +27,11 @@ import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; import org.apache.freemarker.core.templateresolver.TemplateNameFormat; +import org.apache.freemarker.core.templateresolver.TemplateResolver; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2; -import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage; @@ -43,16 +44,24 @@ import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage; public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration { /** - * The {@link TemplateLoader} that is used to look up and load templates. + * The {@link TemplateResolver} to use; defaults to a {@link DefaultTemplateResolver}. + */ + TemplateResolver getTemplateResolver(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateResolverSet(); + + /** + * The {@link TemplateLoader} that is used to look up and load templates; defaults to {@code null}. * By providing your own {@link TemplateLoader} implementation, you can load templates from whatever kind of * storages, like from relational databases, NoSQL-storages, etc. * * <p>You can chain several {@link TemplateLoader}-s together with {@link MultiTemplateLoader}. * - * <p>Default value: You should always set the template loader instead of relying on the default value. - * (But if you still care what it is, before "incompatible improvements" 2.3.21 it's a {@link FileTemplateLoader} - * that uses the current directory as its root; as it's hard tell what that directory will be, it's not very useful - * and dangerous. Starting with "incompatible improvements" 2.3.21 the default is {@code null}.) + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ TemplateLoader getTemplateLoader(); @@ -63,7 +72,11 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration /** * The {@link TemplateLookupStrategy} that is used to look up templates based on the requested name, locale and - * custom lookup condition. Its default is {@link DefaultTemplateLookupStrategy#INSTANCE}. + * custom lookup condition. Its default is {@link DefaultTemplateLookupStrategy#INSTANCE}, except when the + * {@link #getTemplateResolver() templateResolver} doesn't support this setting, in which case it's {@code null}. + * + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ TemplateLookupStrategy getTemplateLookupStrategy(); @@ -74,8 +87,12 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration /** * The template name format used; see {@link TemplateNameFormat}. The default is - * {@link DefaultTemplateNameFormatFM2#INSTANCE}, while the recommended value for new projects is - * {@link DefaultTemplateNameFormat#INSTANCE}. + * {@link DefaultTemplateNameFormatFM2#INSTANCE} (while the recommended value for new projects is + * {@link DefaultTemplateNameFormat#INSTANCE}), except when the {@link #getTemplateResolver() templateResolver} + * doesn't support this setting, in which case it's {@code null}. + * + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ TemplateNameFormat getTemplateNameFormat(); @@ -86,14 +103,17 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration /** * The {@link TemplateConfigurationFactory} that will configure individual templates where their settings differ - * from those coming from the common {@link Configuration} object. A typical use case for that is specifying the - * {@link #getOutputFormat() outputFormat} or {@link #getSourceEncoding() sourceEncoding} for templates based on - * their file extension or parent directory. + * from those coming from the common {@link Configuration} object. Defaults to {@code null}. + * A typical use case for that is specifying the {@link #getOutputFormat() outputFormat} or + * {@link #getSourceEncoding() sourceEncoding} for templates based on their file extension or parent directory. * <p> * Note that the settings suggested by standard file extensions are stronger than that you set here. See * {@link #getRecognizeStandardFileExtensions()} for more information about standard file extensions. * <p> * See "Template configurations" in the FreeMarker Manual for examples. + * + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ TemplateConfigurationFactory getTemplateConfigurations(); @@ -103,8 +123,12 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration boolean isTemplateConfigurationsSet(); /** - * The map-like object used for caching templates to avoid repeated loading and parsing of the template "files". - * Its {@link Configuration}-level default is a {@link SoftCacheStorage}. + * The map-like object used for caching templates to avoid repeated loading and parsing of the template "files" to + * {@link Template} objects. The default is a {@link SoftCacheStorage}, except when the + * {@link #getTemplateResolver() templateResolver} doesn't support this setting, in which case it's {@code null}. + * + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ CacheStorage getCacheStorage(); @@ -115,9 +139,13 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration /** * The time in milliseconds that must elapse before checking whether there is a newer version of a template - * "file" than the cached one. Defaults to 5000 ms. + * "file" than the cached one. The defaults is 5000 ms, except when the + * {@link #getTemplateResolver() templateResolver} doesn't support this setting, in which case it's {@code null}. + * + * <p>If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ - long getTemplateUpdateDelayMilliseconds(); + Long getTemplateUpdateDelayMilliseconds(); /** * Tells if this setting was explicitly set (otherwise its value will be the default value). @@ -158,8 +186,8 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration Version getIncompatibleImprovements(); /** - * Whether localized template lookup is enabled. Enabled by default. - * + * Whether localized template lookup is enabled . The default is {@code true}, except when the + * {@link #getTemplateResolver() templateResolver} doesn't support this setting, in which case it's {@code null}. * <p> * With the default {@link TemplateLookupStrategy}, localized lookup works like this: Let's say your locale setting * is {@code Locale("en", "AU")}, and you call {@link Configuration#getTemplate(String) cfg.getTemplate("foo.ftl")}. @@ -168,8 +196,11 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration * {@link #getTemplateLookupStrategy()} for a more details. If you need to generate different * template names, set your own a {@link TemplateLookupStrategy} implementation as the value of the * {@link #getTemplateLookupStrategy() templateLookupStrategy} setting. + * <p> + * If the {@link #getTemplateResolver() templateResolver} doesn't support this setting, then it must be {@code + * null}. This check is postponed until the {@link Configuration} instance is created. */ - boolean getLocalizedLookup(); + Boolean getLocalizedLookup(); /** * Tells if this setting was explicitly set (otherwise its value will be the default value). http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/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 index e61bb23..b78f49b 100644 --- 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 @@ -22,6 +22,7 @@ import java.io.IOException; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.TemplateConfiguration; +import org.apache.freemarker.core.TopLevelConfiguration; /** * Creates (or returns) {@link TemplateConfiguration}-s for template sources. @@ -35,7 +36,10 @@ public abstract class TemplateConfigurationFactory { * 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()}. + * The object returned by {@link TemplateLoadingResult#getSource()}. For a + * {@link TopLevelConfiguration#getTemplateResolver()} that doesn't use {@link TemplateLoader}-s + * this is maybe {@code null}, or some other object wrapped into {@link TemplateLoadingSource} to + * satisfy this contract. * * @return The {@link TemplateConfiguration} to apply, or {@code null} if the there's no {@link TemplateConfiguration} for * this template source. http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/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 index bfe47e4..5cb5e34 100644 --- 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 @@ -44,7 +44,7 @@ import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; * * <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 + * {@link TemplateLoadingSource} should certainly 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 http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java index c2c5c61..de70101 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java @@ -23,45 +23,93 @@ import java.io.Serializable; import java.util.Locale; import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.ConfigurationException; import org.apache.freemarker.core.ParseException; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.TemplateNotFoundException; import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver; +import org.apache.freemarker.core.util._NullArgumentException; /** - * This class was introduced to allow users to fully implement the template lookup, loading and caching logic, - * in case the standard mechanism ({@link DefaultTemplateResolver}) is not flexible enough. By implementing this class, - * you can take over the duty of the following {@link Configuration} settings, and it's up to the implementation if you - * delegate some of those duties back to the {@link Configuration} setting: - * - * <ul> - * <li>{@link Configuration#getTemplateLoader() templateLoader} - * <li>{@link Configuration#getTemplateNameFormat() templateNameFormat} - * <li>{@link Configuration#getTemplateLookupStrategy() templateLookupStrategy} - * <li>{@link Configuration#getCacheStorage() cacheStorage} - * </ul> + * This class allows user to fully implement the template lookup, loading and caching logic, + * in case the standard mechanism (a {@link DefaultTemplateResolver} combined with all the {@link Configuration} + * settings like {@link Configuration#getTemplateLoader() templateLoader}, + * {@link Configuration#getTemplateConfigurations() templateConfigurations}, etc.) is not flexible enough. + * <p> + * A custom {@link TemplateResolver} can depend on a selected set of {@link Configuration} settings; the same ones that + * {@link DefaultTemplateResolver} depends on. These settings are collected into the + * {@link TemplateResolverDependencies} class, and the {@link TemplateResolver} should get them in {@link #initialize()} + * via {@link #getDependencies()}. It's possible that the custom {@link TemplateResolver} only uses some of these + * settings, which should be reflected by the return value of the {@code supportsXxxDependency} methods (like + * {@link #supportsTemplateLoaderSetting()}). (Note that there's no {@code supportsXxxDependency} method for + * {@link Configuration#getTemplateLanguage() templateLanguage} and {@link Configuration#getSourceEncoding() + * sourceEncoding} and these settings are always exposed.) {@link TemplateResolverDependencies} will also expose the + * {@link TemplateResolverDependencies#parse} method, which is used to create a {@link Template} from its source code. */ -//TODO DRAFT only [FM3] public abstract class TemplateResolver { - private final Configuration configuration; + private TemplateResolverDependencies dependencies; + private boolean initialized; - protected TemplateResolver(Configuration configuration) { - this.configuration = configuration; + /** + * Called by FreeMarker when the {@link Configuration} is built; normally you do not call it yourself. + * This automatically calls {@link #initialize()}. + * + * @throws IllegalStateException If this method was already called with another {@link Configuration} instance. + */ + public final void setDependencies(TemplateResolverDependencies dependencies) { + _NullArgumentException.check("dependencies", dependencies); + synchronized (this) { + // Note that this doesn't make the TemplateResolver safely published (as par Java Memory Model). It only + // guarantees that the configuration won't be changed once it was set. + if (this.dependencies != null) { + if (this.dependencies == dependencies) { + return; + } + throw new IllegalStateException( + "This TemplateResolver is already bound to another Configuration instance."); + } + this.dependencies = dependencies; + + initialize(); + + initialized = true; + } // sync. } - public Configuration getConfiguration() { - return configuration; + /** + * Returns {@code null} before {@link #initialize()} + */ + protected TemplateResolverDependencies getDependencies() { + return dependencies; + } + + /** + * You meant to initialize the instance here instead of in the constructor. This is called only once (by + * FreeMarker at least), when the {@link #setDependencies(TemplateResolverDependencies)} dependencies} is called. + */ + protected abstract void initialize() throws ConfigurationException; + + /** + * Checks if the {@link TemplateResolver} was fully initialized. + * It's a good practice to call this at the beginning of most method. + */ + protected void checkInitialized() { + if (!initialized) { + throw new IllegalStateException( + "TemplateResolver wasn't properly initialized; ensure that initialize() was called and did not " + + "throw an exception."); + } } /** * Retrieves the parsed template with the given name (and according the specified further parameters), or returns a * result that indicates that no such template exists. The result should come from a cache most of the time * (avoiding I/O and template parsing), as this method is typically called frequently. - * * <p> * All parameters must be non-{@code null}, except {@code customLookupCondition}. For the meaning of the parameters - * see {@link Configuration#getTemplate(String, Locale, Serializable, boolean)}. + * see {@link Configuration#getTemplate(String, Locale, Serializable, boolean)}. Note that {@code name} parameter + * is not normalized; you are supposed to call {@link #normalizeRootBasedName(String)} internally. * * @return A {@link GetTemplateResult} object that contains the {@link Template}, or a * {@link GetTemplateResult} object that contains {@code null} as the {@link Template} and information @@ -80,18 +128,15 @@ public abstract class TemplateResolver { * should never be a {@link TemplateNotFoundException}, as that condition is indicated in the return * value. */ - // [FM3] This parameters will be removed: String encoding public abstract GetTemplateResult getTemplate(String name, Locale locale, Serializable customLookupCondition) throws MalformedTemplateNameException, ParseException, IOException; /** * Clears the cache of templates, to enforce re-loading templates when they are get next time; this is an optional * operation. - * * <p> * Note that if the {@link TemplateResolver} implementation uses {@link TemplateLoader}-s, it should also call * {@link TemplateLoader#resetState()} on them. - * * <p> * This method is thread-safe and can be called while the engine processes templates. * @@ -104,10 +149,8 @@ public abstract class TemplateResolver { * Removes a template from the template cache, hence forcing the re-loading of it when it's next time requested; * this is an optional operation. This is to give the application finer control over cache updating than the * {@link Configuration#getTemplateUpdateDelayMilliseconds() templateUpdateDelayMilliseconds} setting alone gives. - * * <p> * For the meaning of the parameters, see {@link #getTemplate(String, Locale, Serializable)} - * * <p> * This method is thread-safe and can be called while the engine processes templates. * @@ -121,10 +164,9 @@ public abstract class TemplateResolver { * Converts a name to a template root directory based name, so that it can be used to find a template without * knowing what (like which template) has referred to it. The rules depend on the name format, but a typical example * is converting "t.ftl" with base "sub/contex.ftl" to "sub/t.ftl". - * * <p> - * Some implementations, notably {@link DefaultTemplateResolver}, delegates this check to the - * {@link TemplateNameFormat} coming from the {@link Configuration}. + * Some implementations, notably {@link DefaultTemplateResolver}, delegate this task to a + * {@link TemplateNameFormat}. * * @param baseName * Maybe a file name, maybe a directory name. The meaning of file name VS directory name depends on the @@ -147,12 +189,11 @@ public abstract class TemplateResolver { * Normalizes a template root directory based name (relative to the root or absolute), so that equivalent names * become equivalent according {@link String#equals(Object)} too. The rules depend on the name format, but typical * examples are "sub/../t.ftl" to "t.ftl", "sub/./t.ftl" to "sub/t.ftl" and "/t.ftl" to "t.ftl". - * * <p> - * Some implementations, notably {@link DefaultTemplateResolver}, delegates this check to the {@link TemplateNameFormat} - * coming from the {@link Configuration}. The standard {@link TemplateNameFormat} implementations shipped with - * FreeMarker always returns a root relative path (except if the name starts with an URI schema, in which case a - * full URI is returned), for example, "/foo.ftl" becomes to "foo.ftl". + * Some implementations, notably {@link DefaultTemplateResolver}, delegates this check to a + * {@link TemplateNameFormat}. The standard {@link TemplateNameFormat} implementations shipped with FreeMarker + * always return a root relative path (except if the name starts with an URI schema, in which case a full URI is + * returned), for example, "/foo.ftl" becomes to "foo.ftl". * * @param name * The root based name. Not {@code null}. @@ -161,4 +202,53 @@ public abstract class TemplateResolver { */ public abstract String normalizeRootBasedName(String name) throws MalformedTemplateNameException; + /** + * Tells whether the {@link TemplateResolver} implementation depends on the + * {@link Configuration#getTemplateLoader() templateLoader} {@link Configuration}. If it returns {@code false} + * then this {@link TemplateResolver} must not call {@link TemplateResolverDependencies#getTemplateLoader()}, or + * else that will throw {@link IllegalStateException}. Furthermore if the user sets the {@code templateLoader} in + * the {@link Configuration} to non-{@code null} value (the default is {@code null}), then the + * {@link Configuration} constructor will throw an exception to tell the user that the {@code templateLoader} + * setting is not supported by this {@link TemplateResolver} class. Some may feel tempted to return {@code true} + * to avoid such error, but consider that as the user has explicitly set this setting, they certainly expect it + * have an effect, and will be frustrated when its ignored. + */ + public abstract boolean supportsTemplateLoaderSetting(); + + /** + * Works like {@link #supportsTemplateLoaderSetting()}, but for the + * {@link Configuration#getCacheStorage() cacheStorage} setting. + */ + public abstract boolean supportsCacheStorageSetting(); + + /** + * Works like {@link #supportsTemplateLoaderSetting()}, but for the + * {@link Configuration#getTemplateLookupStrategy() templateLookupStrategy} setting. + */ + public abstract boolean supportsTemplateLookupStrategySetting(); + + /** + * Works like {@link #supportsTemplateLoaderSetting()}, but for the + * {@link Configuration#getTemplateNameFormat() templateNameFormat} setting. + */ + public abstract boolean supportsTemplateNameFormatSetting(); + + /** + * Works like {@link #supportsTemplateLoaderSetting()}, but for the + * {@link Configuration#getTemplateConfigurations() templateConfigurations} setting. + */ + public abstract boolean supportsTemplateConfigurationsSetting(); + + /** + * Works like {@link #supportsTemplateUpdateDelayMillisecondsSetting()}, but for the + * {@link Configuration#getTemplateUpdateDelayMilliseconds() templateUpdateDelayMilliseconds} setting. + */ + public abstract boolean supportsTemplateUpdateDelayMillisecondsSetting(); + + /** + * Works like {@link #supportsTemplateLoaderSetting()}, but for the + * {@link Configuration#getLocalizedLookup() localizedLookup} setting. + */ + public abstract boolean supportsLocalizedLookupSetting(); + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java new file mode 100644 index 0000000..13820c3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java @@ -0,0 +1,91 @@ +/* + * 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.nio.charset.Charset; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.ParseException; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateConfiguration; +import org.apache.freemarker.core.TemplateLanguage; + +/** + * Stores the dependencies of a {@link TemplateResolver}. See {@link TemplateResolver} for more. + * This is normally implemented by FreeMarker internally. + */ +public abstract class TemplateResolverDependencies { + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsTemplateLoaderSetting()} return {@code false}. + */ + public abstract TemplateLoader getTemplateLoader(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsCacheStorageSetting()} return {@code false}. + */ + public abstract CacheStorage getCacheStorage(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsTemplateLookupStrategySetting()} return + * {@code false}. + */ + public abstract TemplateLookupStrategy getTemplateLookupStrategy(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsTemplateNameFormatSetting()} return + * {@code false}. + */ + public abstract TemplateNameFormat getTemplateNameFormat(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsTemplateConfigurationsSetting()} + * return {@code false}. + */ + public abstract TemplateConfigurationFactory getTemplateConfigurations(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsTemplateUpdateDelayMillisecondsSetting()} + * return {@code false}. + */ + public abstract Long getTemplateUpdateDelayMilliseconds(); + + /** + * @throws IllegalStateException if {@link TemplateResolver#supportsLocalizedLookupSetting()} return {@code false}. + */ + public abstract Boolean getLocalizedLookup(); + + public abstract Charset getSourceEncoding(); + + public abstract TemplateLanguage getTemplateLanguage(); + + /** + * This simply calls {@link TemplateLanguage#parse(String, String, Reader, Configuration, TemplateConfiguration, + * Charset, InputStream)} without exposing the {@link Configuration}. + */ + public abstract Template parse( + TemplateLanguage templateLanguage, String name, String sourceName, Reader reader, + TemplateConfiguration templateConfiguration, Charset encoding, + InputStream streamToUnmarkWhenEncEstabd) throws IOException, ParseException; + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java deleted file mode 100644 index 09b216f..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can - * access things inside this package that users shouldn't. - */ -public final class _CacheAPI { - - private _CacheAPI() { - // Not meant to be instantiated - } - - public static String toRootBasedName(TemplateNameFormat templateNameFormat, String baseName, String targetName) - throws MalformedTemplateNameException { - return templateNameFormat.toRootBasedName(baseName, targetName); - } - - public static String normalizeRootBasedName(TemplateNameFormat templateNameFormat, String name) - throws MalformedTemplateNameException { - return templateNameFormat.normalizeRootBasedName(name); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java index 400693e..a4ac768 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java @@ -19,6 +19,8 @@ package org.apache.freemarker.core.templateresolver.impl; +import static org.apache.freemarker.core.Configuration.ExtendableBuilder.*; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -33,6 +35,8 @@ import java.util.Locale; import java.util.StringTokenizer; import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.ConfigurationException; +import org.apache.freemarker.core.ConfigurationSettingValueException; import org.apache.freemarker.core.Template; import org.apache.freemarker.core.TemplateConfiguration; import org.apache.freemarker.core.TemplateLanguage; @@ -52,6 +56,7 @@ import org.apache.freemarker.core.templateresolver.TemplateLoadingSource; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; import org.apache.freemarker.core.templateresolver.TemplateNameFormat; import org.apache.freemarker.core.templateresolver.TemplateResolver; +import org.apache.freemarker.core.templateresolver.TemplateResolverDependencies; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util.UndeclaredThrowableException; import org.apache.freemarker.core.util._NullArgumentException; @@ -83,100 +88,58 @@ public class DefaultTemplateResolver extends TemplateResolver { private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER; /** Maybe {@code null}. */ - private final TemplateLoader templateLoader; + private TemplateLoader templateLoader; /** Here we keep our cached templates */ - private final CacheStorage cacheStorage; - private final TemplateLookupStrategy templateLookupStrategy; - private final TemplateNameFormat templateNameFormat; - private final TemplateConfigurationFactory templateConfigurations; - private final long templateUpdateDelayMilliseconds; - private final boolean localizedLookup; - - private Configuration config; - - /** - * @param templateLoader - * The {@link TemplateLoader} to use. Can be {@code null}, though then every request will result in - * {@link TemplateNotFoundException}. - * @param cacheStorage - * The {@link CacheStorage} to use. Can't be {@code null}. - * @param templateLookupStrategy - * The {@link TemplateLookupStrategy} to use. Can't be {@code null}. - * @param templateUpdateDelayMilliseconds - * See {@link Configuration#getTemplateUpdateDelayMilliseconds()} - * @param templateNameFormat - * The {@link TemplateNameFormat} to use. Can't be {@code null}. - * @param templateConfigurations - * The {@link TemplateConfigurationFactory} to use. Can be {@code null} (then all templates will use the - * settings coming from the {@link Configuration} as is, except in the very rare case where a - * {@link TemplateLoader} itself specifies a {@link TemplateConfiguration}). - * @param config - * The {@link Configuration} this cache will be used for. Can't be {@code null}. - */ - public DefaultTemplateResolver( - TemplateLoader templateLoader, - CacheStorage cacheStorage, long templateUpdateDelayMilliseconds, - TemplateLookupStrategy templateLookupStrategy, boolean localizedLookup, - TemplateNameFormat templateNameFormat, - TemplateConfigurationFactory templateConfigurations, - Configuration config) { - super(config); - - this.templateLoader = templateLoader; - - _NullArgumentException.check("cacheStorage", cacheStorage); - this.cacheStorage = cacheStorage; + private CacheStorage cacheStorage; + private TemplateLookupStrategy templateLookupStrategy; + private TemplateNameFormat templateNameFormat; + private TemplateConfigurationFactory templateConfigurations; + private long templateUpdateDelayMilliseconds; + private Charset sourceEncoding; + private TemplateLanguage templateLanguage; + private boolean localizedLookup; + + @Override + protected void initialize() throws ConfigurationException { + TemplateResolverDependencies deps = getDependencies(); + + this.templateLoader = deps.getTemplateLoader(); + this.cacheStorage = deps.getCacheStorage(); + checkDependencyNotNull(CACHE_STORAGE_KEY, this.cacheStorage); + + Long templateUpdateDelayMilliseconds = deps.getTemplateUpdateDelayMilliseconds(); + checkDependencyNotNull(TEMPLATE_UPDATE_DELAY_KEY, templateUpdateDelayMilliseconds); this.templateUpdateDelayMilliseconds = templateUpdateDelayMilliseconds; - + + Boolean localizedLookup = deps.getLocalizedLookup(); + checkDependencyNotNull(LOCALIZED_LOOKUP_KEY, localizedLookup); this.localizedLookup = localizedLookup; - - _NullArgumentException.check("templateLookupStrategy", templateLookupStrategy); - this.templateLookupStrategy = templateLookupStrategy; - _NullArgumentException.check("templateNameFormat", templateNameFormat); - this.templateNameFormat = templateNameFormat; + this.templateLookupStrategy = deps.getTemplateLookupStrategy(); + checkDependencyNotNull(TEMPLATE_LOOKUP_STRATEGY_KEY, this.templateLookupStrategy); + + this.templateNameFormat = deps.getTemplateNameFormat(); + checkDependencyNotNull(TEMPLATE_NAME_FORMAT_KEY, this.templateNameFormat); // Can be null - this.templateConfigurations = templateConfigurations; - - _NullArgumentException.check("config", config); - this.config = config; - } - - /** - * Returns the configuration for internal usage. - */ - @Override - public Configuration getConfiguration() { - return config; - } + this.templateConfigurations = deps.getTemplateConfigurations(); - public TemplateLoader getTemplateLoader() { - return templateLoader; - } + this.sourceEncoding = deps.getSourceEncoding(); + checkDependencyNotNull(SOURCE_ENCODING_KEY, this.sourceEncoding); - public CacheStorage getCacheStorage() { - return cacheStorage; + this.templateLanguage = deps.getTemplateLanguage(); + checkDependencyNotNull(TEMPLATE_LANGUAGE_KEY, this.templateLanguage); } - - /** - */ - public TemplateLookupStrategy getTemplateLookupStrategy() { - return templateLookupStrategy; - } - - /** - */ - public TemplateNameFormat getTemplateNameFormat() { - return templateNameFormat; - } - - /** - */ - public TemplateConfigurationFactory getTemplateConfigurations() { - return templateConfigurations; + + private void checkDependencyNotNull(String name, Object value) { + if (value == null) { + throw new ConfigurationSettingValueException( + name, null, false, + "This Configuration setting must be set and non-null when the TemplateResolver is a(n) " + + this.getClass().getName() + ".", null); + } } /** @@ -213,6 +176,8 @@ public class DefaultTemplateResolver extends TemplateResolver { _NullArgumentException.check("name", name); _NullArgumentException.check("locale", locale); + checkInitialized(); + name = templateNameFormat.normalizeRootBasedName(name); if (templateLoader == null) { @@ -233,6 +198,41 @@ public class DefaultTemplateResolver extends TemplateResolver { return templateNameFormat.normalizeRootBasedName(name); } + @Override + public boolean supportsTemplateLoaderSetting() { + return true; + } + + @Override + public boolean supportsCacheStorageSetting() { + return true; + } + + @Override + public boolean supportsTemplateLookupStrategySetting() { + return true; + } + + @Override + public boolean supportsTemplateNameFormatSetting() { + return true; + } + + @Override + public boolean supportsTemplateConfigurationsSetting() { + return true; + } + + @Override + public boolean supportsTemplateUpdateDelayMillisecondsSetting() { + return true; + } + + @Override + public boolean supportsLocalizedLookupSetting() { + return true; + } + private Template getTemplateInternal( final String name, final Locale locale, final Serializable customLookupCondition) throws IOException { @@ -533,9 +533,9 @@ public class DefaultTemplateResolver extends TemplateResolver { locale = tc.getLocale(); } Charset initialEncoding = tc != null && tc.isSourceEncodingSet() ? tc.getSourceEncoding() - : config.getSourceEncoding(); + : this.sourceEncoding; TemplateLanguage templateLanguage = tc != null && tc.isTemplateLanguageSet() ? tc.getTemplateLanguage() - : config.getTemplateLanguage(); + : this.templateLanguage; Template template; { @@ -572,7 +572,8 @@ public class DefaultTemplateResolver extends TemplateResolver { try { try { - template = templateLanguage.parse(name, sourceName, reader, config, tc, + template = getDependencies().parse( + templateLanguage, name, sourceName, reader, tc, initialEncoding, markedInputStream); } catch (WrongTemplateCharsetException charsetException) { final Charset templateSpecifiedEncoding = charsetException.getTemplateSpecifiedEncoding(); @@ -590,7 +591,8 @@ public class DefaultTemplateResolver extends TemplateResolver { + ", but its canSpecifyCharsetInContent property is false."); } - template = templateLanguage.parse(name, sourceName, reader, config, tc, + template = getDependencies().parse( + templateLanguage, name, sourceName, reader, tc, templateSpecifiedEncoding, markedInputStream); } } finally { @@ -604,28 +606,6 @@ public class DefaultTemplateResolver extends TemplateResolver { } /** - * Gets the delay in milliseconds between checking for newer versions of a - * template source. - * @return the current value of the delay - */ - public long getTemplateUpdateDelayMilliseconds() { - // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not. - synchronized (this) { - return templateUpdateDelayMilliseconds; - } - } - - /** - * Returns if localized template lookup is enabled or not. - */ - public boolean getLocalizedLookup() { - // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not. - synchronized (this) { - return localizedLookup; - } - } - - /** * Removes all entries from the cache, forcing reloading of templates on subsequent * {@link #getTemplate(String, Locale, Serializable)} calls. * @@ -654,14 +634,6 @@ public class DefaultTemplateResolver extends TemplateResolver { } } - /** - * Removes an entry from the cache, hence forcing the re-loading of it when it's next time requested. (It doesn't - * delete the template file itself.) This is to give the application finer control over cache updating than the - * update delay ({@link #getTemplateUpdateDelayMilliseconds()}) alone does. - * - * For the meaning of the parameters, see - * {@link Configuration#getTemplate(String, Locale, Serializable, boolean)} - */ @Override public void removeTemplateFromCache( String name, Locale locale, Serializable customLookupCondition)
