ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Freemarker源码分析(5)cache包中其他的类

2021-11-07 23:02:46  阅读:243  来源: 互联网

标签:包中 return String Freemarker private public 源码 null final


2021SC@SDUSC

Freemarker源码分析(5)cache包中其他的类

Freemarker源码分析(5)cache包中其他的类

总览
其他类的类图

_CacheAPI类

代码

public final class _CacheAPI {

    private _CacheAPI() {
        // 不必实例化
    }
    
    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);
    }

    public static String rootBasedNameToAbsoluteName(TemplateNameFormat templateNameFormat, String rootBasedName)
            throws MalformedTemplateNameException {
        return templateNameFormat.rootBasedNameToAbsoluteName(rootBasedName);
    }
    
}

作用:这个类是为了解决Java中缺少模块系统的问题,也就是说,其他FreeMarker包可以访问这个包中用户不应该访问的内容。(此类仅供内部使用)

TemplateCache类

代码

public class TemplateCache {
    
    public static final long DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS = 5000L;
    
    private static final String ASTERISKSTR = "*";
    private static final char ASTERISK = '*';
    private static final char SLASH = '/';
    private static final String LOCALE_PART_SEPARATOR = "_";
    private static final Logger LOG = Logger.getLogger("freemarker.cache");

    private final TemplateLoader templateLoader;

    private final CacheStorage storage;
    private final TemplateLookupStrategy templateLookupStrategy;
    private final TemplateNameFormat templateNameFormat;
    private final TemplateConfigurationFactory templateConfigurations;
    
    private final boolean isStorageConcurrent;
    private long updateDelay = DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS;
    private boolean localizedLookup = true;

    private Configuration config;

    @Deprecated
    public TemplateCache() {
        this(_TemplateAPI.createDefaultTemplateLoader(Configuration.VERSION_2_3_0));
    }

    @Deprecated
    public TemplateCache(TemplateLoader templateLoader) {
        this(templateLoader, (Configuration) null);
    }

    @Deprecated
    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage) {
        this(templateLoader, cacheStorage, null);
    }

    public TemplateCache(TemplateLoader templateLoader, Configuration config) {
        this(templateLoader, _TemplateAPI.createDefaultCacheStorage(Configuration.VERSION_2_3_0), config);
    }

    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage, Configuration config) {
        this(templateLoader, cacheStorage,
                _TemplateAPI.getDefaultTemplateLookupStrategy(Configuration.VERSION_2_3_0),
                _TemplateAPI.getDefaultTemplateNameFormat(Configuration.VERSION_2_3_0),
                config);
    }

    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage,
            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
            Configuration config) {
        this(templateLoader, cacheStorage, templateLookupStrategy, templateNameFormat, null, config);
    }

    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage,
            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
            TemplateConfigurationFactory templateConfigurations,
            Configuration config) {
        this.templateLoader = templateLoader;
        
        NullArgumentException.check("cacheStorage", cacheStorage);
        this.storage = cacheStorage;
        isStorageConcurrent = cacheStorage instanceof ConcurrentCacheStorage &&
                ((ConcurrentCacheStorage) cacheStorage).isConcurrent();
        
        NullArgumentException.check("templateLookupStrategy", templateLookupStrategy);
        this.templateLookupStrategy = templateLookupStrategy;

        NullArgumentException.check("templateNameFormat", templateNameFormat);
        this.templateNameFormat = templateNameFormat;

        // Can be null
        this.templateConfigurations = templateConfigurations;
        
        this.config = config;
    }

    @Deprecated
    public void setConfiguration(Configuration config) {
        this.config = config;
        clear();
    }

    public TemplateLoader getTemplateLoader() {
        return templateLoader;
    }
    
    public CacheStorage getCacheStorage() {
        return storage;
    }
    
    /**
     * @since 2.3.22
     */
    public TemplateLookupStrategy getTemplateLookupStrategy() {
        return templateLookupStrategy;
    }
    
    public TemplateNameFormat getTemplateNameFormat() {
        return templateNameFormat;
    }

    public TemplateConfigurationFactory getTemplateConfigurations() {
        return templateConfigurations;
    }

    public MaybeMissingTemplate getTemplate(String name, Locale locale, Object customLookupCondition,
            String encoding, boolean parseAsFTL)
    throws IOException {
        NullArgumentException.check("name", name);
        NullArgumentException.check("locale", locale);
        NullArgumentException.check("encoding", encoding);
        
        try {
            name = templateNameFormat.normalizeRootBasedName(name);
        } catch (MalformedTemplateNameException e) {
            // If we don't have to emulate backward compatible behavior, then just rethrow it: 
            if (templateNameFormat != TemplateNameFormat.DEFAULT_2_3_0
                    || config.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_4_0) {
                throw e;
            }
            return new MaybeMissingTemplate(null, e);
        }
        
        if (templateLoader == null) {
            return new MaybeMissingTemplate(name, "The TemplateLoader was null.");
        }
        
        Template template = getTemplateInternal(name, locale, customLookupCondition, encoding, parseAsFTL);
        return template != null ? new MaybeMissingTemplate(template) : new MaybeMissingTemplate(name, (String) null);
    }    

    @Deprecated
    public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL)
    throws IOException {
        return getTemplate(name, locale, null, encoding, parseAsFTL).getTemplate();
    }
    
    @Deprecated
    protected static TemplateLoader createLegacyDefaultTemplateLoader() {
        return _TemplateAPI.createDefaultTemplateLoader(Configuration.VERSION_2_3_0);        
    }
    
    private Template getTemplateInternal(
            final String name, final Locale locale, final Object customLookupCondition,
            final String encoding, final boolean parseAsFTL)
    throws IOException {
        final boolean debug = LOG.isDebugEnabled();
        final String debugName = debug
                ? buildDebugName(name, locale, customLookupCondition, encoding, parseAsFTL)
                : null;
        final TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parseAsFTL);
        
        CachedTemplate cachedTemplate;
        if (isStorageConcurrent) {
            cachedTemplate = (CachedTemplate) storage.get(tk);
        } else {
            synchronized (storage) {
                cachedTemplate = (CachedTemplate) storage.get(tk);
            }
        }
        
        final long now = System.currentTimeMillis();
        
        long lastModified = -1L;
        boolean rethrown = false;
        TemplateLookupResult newLookupResult = null;
        try {
            if (cachedTemplate != null) {
                if (now - cachedTemplate.lastChecked < updateDelay) {
                    if (debug) {
                        LOG.debug(debugName + " cached copy not yet stale; using cached.");
                    }
                    // Can be null, indicating a cached negative lookup
                    Object t = cachedTemplate.templateOrException;
                    if (t instanceof Template || t == null) {
                        return (Template) t;
                    } else if (t instanceof RuntimeException) {
                        throwLoadFailedException((RuntimeException) t);
                    } else if (t instanceof IOException) {
                        rethrown = true;
                        throwLoadFailedException((IOException) t);
                    }
                    throw new BugException("t is " + t.getClass().getName());
                }
                
                cachedTemplate = cachedTemplate.cloneCachedTemplate();
                cachedTemplate.lastChecked = now;

                newLookupResult = lookupTemplate(name, locale, customLookupCondition);

                // Template source was removed
                if (!newLookupResult.isPositive()) {
                    if (debug) {
                        LOG.debug(debugName + " no source found.");
                    } 
                    storeNegativeLookup(tk, cachedTemplate, null);
                    return null;
                }

                final Object newLookupResultSource = newLookupResult.getTemplateSource();
                lastModified = templateLoader.getLastModified(newLookupResultSource);
                boolean lastModifiedNotChanged = lastModified == cachedTemplate.lastModified;
                boolean sourceEquals = newLookupResultSource.equals(cachedTemplate.source);
                if (lastModifiedNotChanged && sourceEquals) {
                    if (debug) {
                        LOG.debug(debugName + ": using cached since " + newLookupResultSource + " hasn't changed.");
                    }
                    storeCached(tk, cachedTemplate);
                    return (Template) cachedTemplate.templateOrException;
                } else if (debug) {
                    if (!sourceEquals) {
                        LOG.debug("Updating source because: " + 
                            "sourceEquals=" + sourceEquals + 
                            ", newlyFoundSource=" + StringUtil.jQuoteNoXSS(newLookupResultSource) + 
                            ", cached.source=" + StringUtil.jQuoteNoXSS(cachedTemplate.source));
                    } else if (!lastModifiedNotChanged) {
                        LOG.debug("Updating source because: " + 
                            "lastModifiedNotChanged=" + lastModifiedNotChanged + 
                            ", cached.lastModified=" + cachedTemplate.lastModified + 
                            " != source.lastModified=" + lastModified);
                    }
                }
            } else {
                if (debug) {
                    LOG.debug("Couldn't find template in cache for " + debugName + "; will try to load it.");
                }
                
                cachedTemplate = new CachedTemplate();
                cachedTemplate.lastChecked = now;
                
                newLookupResult = lookupTemplate(name, locale, customLookupCondition);
                
                if (!newLookupResult.isPositive()) {
                    storeNegativeLookup(tk, cachedTemplate, null);
                    return null;
                }
                
                cachedTemplate.lastModified = lastModified = Long.MIN_VALUE;
            }

            Object source = newLookupResult.getTemplateSource();
            cachedTemplate.source = source;
            
            if (debug) {
                LOG.debug("Loading template for " + debugName + " from " + StringUtil.jQuoteNoXSS(source));
            }
            
            lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified;            
            Template template = loadTemplate(
                    templateLoader, source,
                    name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,
                    encoding, parseAsFTL);
            cachedTemplate.templateOrException = template;
            cachedTemplate.lastModified = lastModified;
            storeCached(tk, cachedTemplate);
            return template;
        } catch (RuntimeException e) {
            if (cachedTemplate != null) {
                storeNegativeLookup(tk, cachedTemplate, e);
            }
            throw e;
        } catch (IOException e) {
            if (!rethrown) {
                storeNegativeLookup(tk, cachedTemplate, e);
            }
            throw e;
        } finally {
            if (newLookupResult != null && newLookupResult.isPositive()) {
                templateLoader.closeTemplateSource(newLookupResult.getTemplateSource());
            }
        }
    }

    private static final Method INIT_CAUSE = getInitCauseMethod();
    
    private static final Method getInitCauseMethod() {
        try {
            return Throwable.class.getMethod("initCause", new Class[] { Throwable.class });
        } catch (NoSuchMethodException e) {
            return null;
        }
    }
    
    private IOException newIOException(String message, Throwable cause) {
        if (cause == null) {
            return new IOException(message);
        }
        
        IOException ioe;
        if (INIT_CAUSE != null) {
            ioe = new IOException(message);
            try {
                INIT_CAUSE.invoke(ioe, cause);
            } catch (RuntimeException ex) {
                throw ex;
            } catch (Exception ex) {
                throw new UndeclaredThrowableException(ex);
            }
        } else {
            ioe = new IOException(message + "\nCaused by: " + cause.getClass().getName() + 
            ": " + cause.getMessage());
        }
        return ioe;
    }
    
    private void throwLoadFailedException(Throwable e) throws IOException {
        throw newIOException("There was an error loading the " +
                "template on an earlier attempt; see cause exception.", e);
    }

    private void storeNegativeLookup(TemplateKey tk, 
            CachedTemplate cachedTemplate, Exception e) {
        cachedTemplate.templateOrException = e;
        cachedTemplate.source = null;
        cachedTemplate.lastModified = 0L;
        storeCached(tk, cachedTemplate);
    }

    private void storeCached(TemplateKey tk, CachedTemplate cachedTemplate) {
        if (isStorageConcurrent) {
            storage.put(tk, cachedTemplate);
        } else {
            synchronized (storage) {
                storage.put(tk, cachedTemplate);
            }
        }
    }

    private Template loadTemplate(
            final TemplateLoader templateLoader, final Object source,
            final String name, final String sourceName, Locale locale, final Object customLookupCondition,
            String initialEncoding, final boolean parseAsFTL) throws IOException {
        final TemplateConfiguration tc;
        try {
            tc = templateConfigurations != null ? templateConfigurations.get(sourceName, source) : null;
        } catch (TemplateConfigurationFactoryException e) {
            throw newIOException("Error while getting TemplateConfiguration; see cause exception.", e);
        }
        if (tc != null) {
            // TC.{encoding,locale} is stronger than the cfg.getTemplate arguments by design.
            if (tc.isEncodingSet()) {
                initialEncoding = tc.getEncoding();
            }
            if (tc.isLocaleSet()) {
                locale = tc.getLocale();
            }
        }
        
        Template template;
        {
            if (parseAsFTL) {
                try {
                    try (Reader reader = templateLoader.getReader(source, initialEncoding)) {
                        template = new Template(name, sourceName, reader, config, tc, initialEncoding);
                    }
                } catch (Template.WrongEncodingException wee) {
                    String actualEncoding = wee.getTemplateSpecifiedEncoding();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Initial encoding \"" + initialEncoding + "\" was incorrect, re-reading with \""
                                + actualEncoding + "\". Template: " + sourceName);
                    }

                    try (Reader reader = templateLoader.getReader(source, actualEncoding)) {
                        template = new Template(name, sourceName, reader, config, tc, actualEncoding);
                    }
                }
            } else {
                final StringWriter sw = new StringWriter();
                final char[] buf = new char[4096];
                try (Reader reader = templateLoader.getReader(source, initialEncoding)) {
                    fetchChars:
                    while (true) {
                        int charsRead = reader.read(buf);
                        if (charsRead > 0) {
                            sw.write(buf, 0, charsRead);
                        } else if (charsRead < 0) {
                            break fetchChars;
                        }
                    }
                }
                template = Template.getPlainTextTemplate(name, sourceName, sw.toString(), config);
                template.setEncoding(initialEncoding);
            }
        }

        if (tc != null) {
            tc.apply(template);
        }
        
        template.setLocale(locale);
        template.setCustomLookupCondition(customLookupCondition);
        return template;
    }

    public long getDelay() {
        synchronized (this) {
            return updateDelay;
        }
    }

    public void setDelay(long delay) {
        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
        synchronized (this) {
            this.updateDelay = delay;
        }
    }

    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;
        }
    }

    public void setLocalizedLookup(boolean localizedLookup) {
        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
        synchronized (this) {
            if (this.localizedLookup != localizedLookup) {
                this.localizedLookup = localizedLookup;
                clear();
            }
        }
    }

    public void clear() {
        synchronized (storage) {
            storage.clear();
            if (templateLoader instanceof StatefulTemplateLoader) {
                ((StatefulTemplateLoader) templateLoader).resetState();
            }
        }
    }

    /**
     * Same as {@link #removeTemplate(String, Locale, Object, String, boolean)} with {@code null}
     * {@code customLookupCondition}.
     */
    public void removeTemplate(
            String name, Locale locale, String encoding, boolean parse) throws IOException {
        removeTemplate(name, locale, null, encoding, parse);
    }
    
    public void removeTemplate(
            String name, Locale locale, Object customLookupCondition, String encoding, boolean parse)
    throws IOException {
        if (name == null) {
            throw new IllegalArgumentException("Argument \"name\" can't be null");
        }
        if (locale == null) {
            throw new IllegalArgumentException("Argument \"locale\" can't be null");
        }
        if (encoding == null) {
            throw new IllegalArgumentException("Argument \"encoding\" can't be null");
        }
        name = templateNameFormat.normalizeRootBasedName(name);
        if (name != null && templateLoader != null) {
            boolean debug = LOG.isDebugEnabled();
            String debugName = debug
                    ? buildDebugName(name, locale, customLookupCondition, encoding, parse)
                    : null;
            TemplateKey tk = new TemplateKey(name, locale, customLookupCondition, encoding, parse);
            
            if (isStorageConcurrent) {
                storage.remove(tk);
            } else {
                synchronized (storage) {
                    storage.remove(tk);
                }
            }
            if (debug) {
                LOG.debug(debugName + " was removed from the cache, if it was there");
            }
        }
    }

    private String buildDebugName(String name, Locale locale, Object customLookupCondition, String encoding,
            boolean parse) {
        return StringUtil.jQuoteNoXSS(name) + "("
                + StringUtil.jQuoteNoXSS(locale)
                + (customLookupCondition != null ? ", cond=" + StringUtil.jQuoteNoXSS(customLookupCondition) : "")
                + ", " + encoding
                + (parse ? ", parsed)" : ", unparsed]");
    }    

    @Deprecated
    public static String getFullTemplatePath(Environment env, String baseName, String targetName) {
        try {
            return env.toFullTemplateName(baseName, targetName);
        } catch (MalformedTemplateNameException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    private TemplateLookupResult lookupTemplate(String name, Locale locale, Object customLookupCondition)
            throws IOException {
        final TemplateLookupResult lookupResult = templateLookupStrategy.lookup(
                new TemplateCacheTemplateLookupContext(name, locale, customLookupCondition));
        if (lookupResult == null) {
            throw new NullPointerException("Lookup result shouldn't be null");
        }
        return lookupResult;
    }

    private TemplateLookupResult lookupTemplateWithAcquisitionStrategy(String path) throws IOException {
        int asterisk = path.indexOf(ASTERISK);
        // Shortcut in case there is no acquisition
        if (asterisk == -1) {
            return TemplateLookupResult.from(path, findTemplateSource(path));
        }
        StringTokenizer tok = new StringTokenizer(path, "/");
        int lastAsterisk = -1;
        List tokpath = new ArrayList();
        while (tok.hasMoreTokens()) {
            String pathToken = tok.nextToken();
            if (pathToken.equals(ASTERISKSTR)) {
                if (lastAsterisk != -1) {
                    tokpath.remove(lastAsterisk);
                }
                lastAsterisk = tokpath.size();
            }
            tokpath.add(pathToken);
        }
        if (lastAsterisk == -1) {  // if there was no real "*" step after all
            return TemplateLookupResult.from(path, findTemplateSource(path));
        }
        String basePath = concatPath(tokpath, 0, lastAsterisk);
        String resourcePath = concatPath(tokpath, lastAsterisk + 1, tokpath.size());
        if (resourcePath.endsWith("/")) {
            resourcePath = resourcePath.substring(0, resourcePath.length() - 1);
        }
        StringBuilder buf = new StringBuilder(path.length()).append(basePath);
        int l = basePath.length();
        for (; ; ) {
            String fullPath = buf.append(resourcePath).toString();
            Object templateSource = findTemplateSource(fullPath);
            if (templateSource != null) {
                return TemplateLookupResult.from(fullPath, templateSource);
            }
            if (l == 0) {
                return TemplateLookupResult.createNegativeResult();
            }
            l = basePath.lastIndexOf(SLASH, l - 2) + 1;
            buf.setLength(l);
        }
    }

    private Object findTemplateSource(String path) throws IOException {
        final Object result = templateLoader.findTemplateSource(path);
        if (LOG.isDebugEnabled()) {
            LOG.debug("TemplateLoader.findTemplateSource(" +  StringUtil.jQuote(path) + "): "
                    + (result == null ? "Not found" : "Found"));
        }
        return modifyForConfIcI(result);
    }

    private Object modifyForConfIcI(Object templateSource) {
        if (templateSource == null) return null;
        
        if (config.getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_21) {
            return templateSource;
        }
        
        if (templateSource instanceof URLTemplateSource) {
            URLTemplateSource urlTemplateSource = (URLTemplateSource) templateSource;
            if (urlTemplateSource.getUseCaches() == null) {  // It was left unset
                urlTemplateSource.setUseCaches(false);
            }
        } else if (templateSource instanceof MultiSource) {
            modifyForConfIcI(((MultiSource) templateSource).getWrappedSource());
        }
        return templateSource;
    }

    private String concatPath(List path, int from, int to) {
        StringBuilder buf = new StringBuilder((to - from) * 16);
        for (int i = from; i < to; ++i) {
            buf.append(path.get(i)).append('/');
        }
        return buf.toString();
    }
    
    private static final class TemplateKey {
        private final String name;
        private final Locale locale;
        private final Object customLookupCondition;
        private final String encoding;
        private final boolean parse;

        TemplateKey(String name, Locale locale, Object customLookupCondition, String encoding, boolean parse) {
            this.name = name;
            this.locale = locale;
            this.customLookupCondition = customLookupCondition;
            this.encoding = encoding;
            this.parse = parse;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof TemplateKey) {
                TemplateKey tk = (TemplateKey) o;
                return
                    parse == tk.parse &&
                    name.equals(tk.name) &&
                    locale.equals(tk.locale) &&
                    nullSafeEquals(customLookupCondition, tk.customLookupCondition) &&
                    encoding.equals(tk.encoding);
            }
            return false;
        }

        private boolean nullSafeEquals(Object o1, Object o2) {
            return o1 != null
                ? (o2 != null ? o1.equals(o2) : false)
                : o2 == null;
        }

        @Override
        public int hashCode() {
            return
                name.hashCode() ^
                locale.hashCode() ^
                encoding.hashCode() ^
                (customLookupCondition != null ? customLookupCondition.hashCode() : 0) ^
                Boolean.valueOf(!parse).hashCode();
        }
    }

    private static final class CachedTemplate implements Cloneable, Serializable {
        private static final long serialVersionUID = 1L;

        Object templateOrException;
        Object source;
        long lastChecked;
        long lastModified;
        
        public CachedTemplate cloneCachedTemplate() {
            try {
                return (CachedTemplate) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new UndeclaredThrowableException(e);
            }
        }
    }
    
    private class TemplateCacheTemplateLookupContext extends TemplateLookupContext {

        TemplateCacheTemplateLookupContext(String templateName, Locale templateLocale, Object customLookupCondition) {
            super(templateName, localizedLookup ? templateLocale : null, customLookupCondition);
        }

        @Override
        public TemplateLookupResult lookupWithAcquisitionStrategy(String name) throws IOException {
            // Only one of the possible ways of making a name non-normalized, but is the easiest mistake to do:
            if (name.startsWith("/")) {
                throw new IllegalArgumentException("Non-normalized name, starts with \"/\": " + name);
            }
            
            return TemplateCache.this.lookupTemplateWithAcquisitionStrategy(name);
        }

        @Override
        public TemplateLookupResult lookupWithLocalizedThenAcquisitionStrategy(final String templateName,
                final Locale templateLocale) throws IOException {
            
                if (templateLocale == null) {
                    return lookupWithAcquisitionStrategy(templateName);
                }
                
                int lastDot = templateName.lastIndexOf('.');
                String prefix = lastDot == -1 ? templateName : templateName.substring(0, lastDot);
                String suffix = lastDot == -1 ? "" : templateName.substring(lastDot);
                String localeName = LOCALE_PART_SEPARATOR + templateLocale.toString();
                StringBuilder buf = new StringBuilder(templateName.length() + localeName.length());
                buf.append(prefix);
                tryLocaleNameVariations: while (true) {
                    buf.setLength(prefix.length());
                    String path = buf.append(localeName).append(suffix).toString();
                    TemplateLookupResult lookupResult = lookupWithAcquisitionStrategy(path);
                    if (lookupResult.isPositive()) {
                        return lookupResult;
                    }
                    
                    int lastUnderscore = localeName.lastIndexOf('_');
                    if (lastUnderscore == -1) {
                        break tryLocaleNameVariations;
                    }
                    localeName = localeName.substring(0, lastUnderscore);
                }
                return createNegativeLookupResult();
        }
        
    }
    
 
    public final static class MaybeMissingTemplate {
        
        private final Template template;
        private final String missingTemplateNormalizedName;
        private final String missingTemplateReason;
        private final MalformedTemplateNameException missingTemplateCauseException;
        
        private MaybeMissingTemplate(Template template) {
            this.template = template;
            this.missingTemplateNormalizedName = null;
            this.missingTemplateReason = null;
            this.missingTemplateCauseException = null;
        }
        
        private MaybeMissingTemplate(String normalizedName, MalformedTemplateNameException missingTemplateCauseException) {
            this.template = null;
            this.missingTemplateNormalizedName = normalizedName;
            this.missingTemplateReason = null;
            this.missingTemplateCauseException = missingTemplateCauseException;
        }
        
        private MaybeMissingTemplate(String normalizedName, String missingTemplateReason) {
            this.template = null;
            this.missingTemplateNormalizedName = normalizedName;
            this.missingTemplateReason = missingTemplateReason;
            this.missingTemplateCauseException = null;
        }
        
        public Template getTemplate() {
            return template;
        }

        public String getMissingTemplateReason() {
            return missingTemplateReason != null
                    ? missingTemplateReason
                    : (missingTemplateCauseException != null
                            ? missingTemplateCauseException.getMalformednessDescription()
                            : null);
        }
        
        public String getMissingTemplateNormalizedName() {
            return missingTemplateNormalizedName;
        }
        
    }
    
}

作用:执行模板的缓存和按需加载。实际的模板“文件”加载被委托给一个TemplateLoader对象,你可以在构造函数中指定它。缓存的某些方面委托给CacheStorage对象,你也可以在构造函数中指定。

URLTemplateSource类

代码

class URLTemplateSource {
    private final URL url;
    private URLConnection conn;
    private InputStream inputStream;
    private Boolean useCaches;

    URLTemplateSource(URL url, Boolean useCaches) throws IOException {
        this.url = url;
        this.conn = url.openConnection();
        this.useCaches = useCaches;
        if (useCaches != null) {
            conn.setUseCaches(useCaches.booleanValue());
        }
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof URLTemplateSource) {
            return url.equals(((URLTemplateSource) o).url);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return url.hashCode();
    }

    @Override
    public String toString() {
        return url.toString();
    }
    
    long lastModified() {
        if (conn instanceof JarURLConnection) {
          URL jarURL = ((JarURLConnection) conn).getJarFileURL();
          if (jarURL.getProtocol().equals("file")) {
            return new File(jarURL.getFile()).lastModified();
          } else {
            // Use the URL mechanism
            URLConnection jarConn = null;
            try {
              jarConn = jarURL.openConnection();
              return jarConn.getLastModified();
            } catch (IOException e) {
              return -1;
            } finally {
              try {
                if (jarConn != null) jarConn.getInputStream().close();
              } catch (IOException e) { }
            }
          }
        } else {
          long lastModified = conn.getLastModified();
          if (lastModified == -1L && url.getProtocol().equals("file")) {
              return new File(url.getFile()).lastModified();
          } else {
              return lastModified;
          }
        }
    }

    InputStream getInputStream() throws IOException {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
            }
            this.conn = url.openConnection();
        }
        inputStream = conn.getInputStream();
        return inputStream;
    }

    void close() throws IOException {
        try {
          if (inputStream != null) {
              inputStream.close();
          } else {
              conn.getInputStream().close();
          }
        } finally {
          inputStream = null;
          conn = null;
        }
    }

    Boolean getUseCaches() {
        return useCaches;
    }

    void setUseCaches(boolean useCaches) {
        if (this.conn != null) {
            conn.setUseCaches(useCaches);
            this.useCaches = Boolean.valueOf(useCaches);
        }
    }
    
}

作用:包装一个URL对象,并实现一个典型模板源所需的方法。

TemplateLookupStrategy类

代码

public abstract class TemplateLookupStrategy {

    public static final TemplateLookupStrategy DEFAULT_2_3_0 = new Default020300();
    
    public abstract TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException;
    
    private static class Default020300 extends TemplateLookupStrategy {
        
        @Override
        public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException {
            return ctx.lookupWithLocalizedThenAcquisitionStrategy(ctx.getTemplateName(), ctx.getTemplateLocale());
        }
        
        @Override
        public String toString() {
            return "TemplateLookupStrategy.DEFAULT_2_3_0";
        }
        
    }
    
}

作用:为请求模板的模板名找到TemplateLoader-level(存储级)模板源(如在Configuration.getTemplate(String)中)。这通常意味着尝试各种TemplateLoader对象级别的模板名称(也就是所谓的源名称;参见Template.getSourceName())是从请求的名称推导出来的。

TemplateLookupContext类

代码

public abstract class TemplateLookupContext {
    
    private final String templateName;
    private final Locale templateLocale;
    private final Object customLookupCondition;

    public abstract TemplateLookupResult lookupWithAcquisitionStrategy(String templateName) throws IOException;

    public abstract TemplateLookupResult lookupWithLocalizedThenAcquisitionStrategy(String templateName,
            Locale templateLocale) throws IOException;
    
    TemplateLookupContext(String templateName, Locale templateLocale, Object customLookupCondition) {
        this.templateName = templateName;
        this.templateLocale = templateLocale;
        this.customLookupCondition = customLookupCondition;
    }

    public String getTemplateName() {
        return templateName;
    }

    public Locale getTemplateLocale() {
        return templateLocale;
    }

    public Object getCustomLookupCondition() {
        return customLookupCondition;
    }

    public TemplateLookupResult createNegativeLookupResult() {
        return TemplateLookupResult.createNegativeResult();
    }
    
}

作用:用作TemplateLookupStrategy.lookup(TemplateLookupContext)的参数。不能创建它的实例,只能从FreeMarker接收它们。

TemplateLookupResult类

代码

public abstract class TemplateLookupResult {

    static TemplateLookupResult createNegativeResult() {
        return NegativeTemplateLookupResult.INSTANCE;
    }
    
    /** Used internally to create the appropriate kind of result from the parameters. */
    static TemplateLookupResult from(String templateSourceName, Object templateSource) {
        return templateSource != null
                ? new PositiveTemplateLookupResult(templateSourceName, templateSource)
                : createNegativeResult();
    }
    
    private TemplateLookupResult() {
        // nop
    }

    public abstract String getTemplateSourceName();

    public abstract boolean isPositive();

    abstract Object getTemplateSource();

    private static final class PositiveTemplateLookupResult extends TemplateLookupResult {

        private final String templateSourceName;
        private final Object templateSource;

        private PositiveTemplateLookupResult(String templateSourceName, Object templateSource) {
            NullArgumentException.check("templateName", templateSourceName);
            NullArgumentException.check("templateSource", templateSource);

            if (templateSource instanceof TemplateLookupResult) {
                throw new IllegalArgumentException();
            }

            this.templateSourceName = templateSourceName;
            this.templateSource = templateSource;
        }

        @Override
        public String getTemplateSourceName() {
            return templateSourceName;
        }

        @Override
        Object getTemplateSource() {
            return templateSource;
        }

        @Override
        public boolean isPositive() {
            return true;
        }
    }

    private static final class NegativeTemplateLookupResult extends TemplateLookupResult {
        
        private static final NegativeTemplateLookupResult INSTANCE = new NegativeTemplateLookupResult();
                
        private NegativeTemplateLookupResult() {
            // nop
        }

        @Override
        public String getTemplateSourceName() {
            return null;
        }

        @Override
        Object getTemplateSource() {
            return null;
        }

        @Override
        public boolean isPositive() {
            return false;
        }
        
    }
    
}

作用:TemplateLookupStrategy.lookup(TemplateLookupContext)和类似的查找方法的返回值,通常由TemplateLookupContext.lookupWithAcquisitionStrategy(String)或者TemplateLookupContext.createNegativeLookupResult()获取此类的对象,不能直接创建shi它的实例。

注:Freemarker代码来自FreeMarker 中文官方参考手册

新手写的代码分析,文章若有错误还请指出

标签:包中,return,String,Freemarker,private,public,源码,null,final
来源: https://blog.csdn.net/qq_43518847/article/details/121198511

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有