This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch FREEMARKER-35
in repository https://gitbox.apache.org/repos/asf/freemarker.git


The following commit(s) were added to refs/heads/FREEMARKER-35 by this push:
     new b42a533c FREEMARKER-35: Added caching to 
JavaTemplateTemporalFormatFactory
b42a533c is described below

commit b42a533c7d10b47bf77439adc5c0055924557f80
Author: ddekany <[email protected]>
AuthorDate: Sat Aug 27 19:19:20 2022 +0200

    FREEMARKER-35: Added caching to JavaTemplateTemporalFormatFactory
---
 .../java/freemarker/core/FastLRUKeyValueStore.java |  5 +++
 .../core/JavaTemplateTemporalFormatFactory.java    | 45 +++++++++++++++++++++-
 ...rmatByFormatStringCachingInEnvironmentTest.java |  4 ++
 ...poralFormatCurrentCachingInEnvironmentTest.java | 38 ++++++++++--------
 4 files changed, 74 insertions(+), 18 deletions(-)

diff --git a/src/main/java/freemarker/core/FastLRUKeyValueStore.java 
b/src/main/java/freemarker/core/FastLRUKeyValueStore.java
index db774162..9cbf465a 100644
--- a/src/main/java/freemarker/core/FastLRUKeyValueStore.java
+++ b/src/main/java/freemarker/core/FastLRUKeyValueStore.java
@@ -82,4 +82,9 @@ class FastLRUKeyValueStore<K, V> {
 
         return putIfAbsentThenReturnStored(cacheKey, value);
     }
+
+    void clear() {
+        olderEntries.clear();
+        recentEntries.clear();
+    }
 }
diff --git 
a/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java 
b/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
index ac4228bf..2ad345c3 100644
--- a/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
+++ b/src/main/java/freemarker/core/JavaTemplateTemporalFormatFactory.java
@@ -21,11 +21,15 @@ package freemarker.core;
 
 import java.time.temporal.Temporal;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.TimeZone;
 
 class JavaTemplateTemporalFormatFactory extends TemplateTemporalFormatFactory {
     public static final JavaTemplateTemporalFormatFactory INSTANCE = new 
JavaTemplateTemporalFormatFactory();
 
+    private final FastLRUKeyValueStore<CacheKey, JavaTemplateTemporalFormat> 
formatCache =
+            new FastLRUKeyValueStore<>(512);
+
     private JavaTemplateTemporalFormatFactory() {
         // Not instantiated from outside
     }
@@ -34,7 +38,46 @@ class JavaTemplateTemporalFormatFactory extends 
TemplateTemporalFormatFactory {
     public TemplateTemporalFormat get(
             String params, Class<? extends Temporal> temporalClass, Locale 
locale, TimeZone timeZone, Environment env)
             throws TemplateValueFormatException {
-        return new JavaTemplateTemporalFormat(params, temporalClass, locale, 
timeZone);
+        CacheKey cacheKey = new CacheKey(params, temporalClass, locale, 
timeZone);
+        JavaTemplateTemporalFormat format = formatCache.get(cacheKey);
+        if (format == null) {
+            format = new JavaTemplateTemporalFormat(params, temporalClass, 
locale, timeZone);
+            format = formatCache.putIfAbsentThenReturnStored(cacheKey, format);
+        }
+        // JavaTemplateTemporalFormat-s use a 
java.time.format.DateTimeFormatter internally, and so are thread safe,
+        // and immutable, so we don't have to clone() anything (unlike in 
JavaTemplateDateFormatFactory):
+        return format;
+    }
+
+    void clear() {
+        formatCache.clear();
     }
 
+    private static class CacheKey {
+        private final String params;
+        private final Class<? extends Temporal> temporalClass;
+        private final Locale locale;
+        private final TimeZone timeZone;
+
+        public CacheKey(String params, Class<? extends Temporal> 
temporalClass, Locale locale, TimeZone timeZone) {
+            this.params = params;
+            this.temporalClass = temporalClass;
+            this.locale = locale;
+            this.timeZone = timeZone;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            CacheKey cacheKey = (CacheKey) o;
+            return params.equals(cacheKey.params) && 
temporalClass.equals(cacheKey.temporalClass) &&
+                    locale.equals(cacheKey.locale) && 
timeZone.equals(cacheKey.timeZone);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(params, temporalClass, locale, timeZone);
+        }
+    }
 }
diff --git 
a/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
 
b/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
index cda9eb74..262fd4c3 100644
--- 
a/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
+++ 
b/src/test/java/freemarker/core/TemplateTemporalFormatByFormatStringCachingInEnvironmentTest.java
@@ -170,12 +170,14 @@ public class 
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
 
         for (int i = 1; i <= 3; i++) {
             env.setLocale(locales.get(locales.size() - i));
+            JavaTemplateTemporalFormatFactory.INSTANCE.clear();
             assertSame(
                     env.getTemplateTemporalFormat(formatString, 
LocalDate.class),
                     onceCachedFormats.get(onceCachedFormats.size() - i));
         }
 
         env.setLocale(locales.get(0));
+        JavaTemplateTemporalFormatFactory.INSTANCE.clear();
         assertNotSame(
                 env.getTemplateTemporalFormat(formatString, LocalDate.class),
                 onceCachedFormats.get(0));
@@ -202,6 +204,7 @@ public class 
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
             TemplateValueFormatException {
         for (int valueIndex = 0; valueIndex < 
settingAssignments.numberOfValues(); valueIndex++) {
             settingAssignments.execute(env, valueIndex);
+            JavaTemplateTemporalFormatFactory.INSTANCE.clear();
             assertSame(env.getTemplateTemporalFormat(temporalClass), 
cachedFormats.get(valueIndex));
         }
     }
@@ -212,6 +215,7 @@ public class 
TemplateTemporalFormatByFormatStringCachingInEnvironmentTest
             TemplateValueFormatException {
         for (int valueIndex = 0; valueIndex < 
settingAssignments.numberOfValues(); valueIndex++) {
             String formatString = settingAssignments.getValue(valueIndex);
+            JavaTemplateTemporalFormatFactory.INSTANCE.clear();
             assertSame(env.getTemplateTemporalFormat(formatString, 
temporalClass), cachedFormats.get(valueIndex));
         }
     }
diff --git 
a/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
 
b/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
index 86db4b05..dc02becf 100644
--- 
a/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
+++ 
b/src/test/java/freemarker/core/TemplateTemporalFormatCurrentCachingInEnvironmentTest.java
@@ -161,50 +161,46 @@ public class 
TemplateTemporalFormatCurrentCachingInEnvironmentTest
         TemplateTemporalFormat lastFormat;
         TemplateTemporalFormat newFormat;
 
-        // Note: We call 
env.clearCachedTemplateTemporalFormatsByFormatString() directly before all
-        // env.getTemplateTemporalFormat calls, just to avoid that 2nd level 
of cache hiding any bugs in the level
-        // that we want to test here. But in almost all of these tests 
scenarios it shouldn't have any effect anyway.
-
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         lastFormat = env.getTemplateTemporalFormat(temporalClass);
         // Assert that it keeps returning the same instance from cache:
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
 
         settingAssignments.execute(env, 0);
         // Assert that the cache wasn't cleared when the setting was set to 
the same value again:
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
 
         env.setLocale(Locale.JAPAN); // Possibly clears non-reusable 
TemplateTemporalFormatCache field
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         newFormat = env.getTemplateTemporalFormat(temporalClass);
         if (localeDependent) {
             assertNotSame(lastFormat, newFormat);
         } else {
-            env.clearCachedTemplateTemporalFormatsByFormatString();
+            clearHigherLevelCaches(env);
             assertSame(lastFormat, 
env.getTemplateTemporalFormat(temporalClass));
         }
         lastFormat = newFormat;
 
         env.setLocale(Locale.JAPAN);
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
 
         env.setLocale(Locale.GERMANY); // Possibly clears non-reusable 
TemplateTemporalFormatCache field
         env.setLocale(Locale.JAPAN);
         // Assert that it restores the same instance from 
TemplateTemporalFormatCache.reusableXxx field:
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
 
         env.setTimeZone(OTHER_TIME_ZONE); // Possibly clears non-reusable 
TemplateTemporalFormatCache field
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         newFormat = env.getTemplateTemporalFormat(temporalClass);
         if (timeZoneDependent) {
             assertNotSame(newFormat, lastFormat);
-            env.clearCachedTemplateTemporalFormatsByFormatString();
+            clearHigherLevelCaches(env);
             assertSame(newFormat, 
env.getTemplateTemporalFormat(temporalClass));
         } else {
             assertSame(newFormat, lastFormat);
@@ -213,19 +209,27 @@ public class 
TemplateTemporalFormatCurrentCachingInEnvironmentTest
 
         env.setTimeZone(DateUtil.UTC); // Possibly clears non-reusable 
TemplateTemporalFormatCache field
         env.setTimeZone(OTHER_TIME_ZONE);
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         // Assert that it restores the same instance from 
TemplateTemporalFormatCache.reusableXxx field:
         assertSame(lastFormat, env.getTemplateTemporalFormat(temporalClass));
 
         settingAssignments.execute(env, 1); // Clears even 
TemplateTemporalFormatCache.reusableXxx
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         newFormat = env.getTemplateTemporalFormat(temporalClass);
         assertNotSame(lastFormat, newFormat);
 
         settingAssignments.execute(env, 0); // Clears even 
TemplateTemporalFormatCache.reusableXxx
-        env.clearCachedTemplateTemporalFormatsByFormatString();
+        clearHigherLevelCaches(env);
         newFormat = env.getTemplateTemporalFormat(temporalClass);
         assertNotSame(lastFormat, newFormat);
     }
 
+    /**
+     * Clears caches that are higher level than the "current format", so that 
we can test the last reliably.
+     */
+    private void clearHigherLevelCaches(Environment env) {
+        JavaTemplateTemporalFormatFactory.INSTANCE.clear();
+        env.clearCachedTemplateTemporalFormatsByFormatString();
+    }
+
 }

Reply via email to