Skip to content

Commit 337b463

Browse files
authored
Merge pull request #55 from salesforce/replace-guava-with-caffeine
Replace Guava LoadingCache with Caffeine
2 parents 7a48eb1 + 4cd610c commit 337b463

File tree

4 files changed

+80
-56
lines changed

4 files changed

+80
-56
lines changed

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<icu4j.version>69.1</icu4j.version>
4747
<icu4j-localespi.version>69.1</icu4j-localespi.version>
4848
<picocli.version>4.1.4</picocli.version>
49+
<caffeine.version>2.8.0</caffeine.version>
4950
</properties>
5051

5152
<profiles>
@@ -298,6 +299,16 @@
298299
<optional>true</optional>
299300
</dependency>
300301

302+
<dependency>
303+
<groupId>com.github.ben-manes.caffeine</groupId>
304+
<artifactId>caffeine</artifactId>
305+
<version>${caffeine.version}</version>
306+
</dependency>
307+
<dependency>
308+
<groupId>com.github.ben-manes.caffeine</groupId>
309+
<artifactId>guava</artifactId>
310+
<version>${caffeine.version}</version>
311+
</dependency>
301312

302313
<dependency>
303314
<groupId>org.freemarker</groupId>
Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,75 @@
1-
/*
1+
/*
22
* Copyright (c) 2019, salesforce.com, inc.
33
* All rights reserved.
4-
* Licensed under the BSD 3-Clause license.
4+
* Licensed under the BSD 3-Clause license.
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
package com.force.i18n;
88

99
import java.util.Set;
1010
import java.util.stream.Collectors;
1111

12+
import com.github.benmanes.caffeine.cache.Caffeine;
13+
import com.github.benmanes.caffeine.guava.CaffeinatedGuava;
1214
import com.google.common.cache.*;
1315
import com.ibm.icu.text.PluralRules;
1416
import com.ibm.icu.text.PluralRules.PluralType;
1517

1618
/**
1719
* Default implementation of LanguagePluralRules that uses ICU4J.
18-
*
20+
*
1921
* @author stamm
2022
* @since 1.1
2123
*/
2224
class DefaultLanguagePluralRulesImpl implements LanguagePluralRules {
23-
private final HumanLanguage language;
24-
private final PluralRules cardinal;
25-
private final PluralRules ordinal;
26-
public DefaultLanguagePluralRulesImpl(HumanLanguage language) {
27-
this.language = language;
28-
this.cardinal = PluralRules.forLocale(language.getLocale(), PluralType.CARDINAL);
29-
this.ordinal = PluralRules.forLocale(language.getLocale(), PluralType.ORDINAL);
30-
}
25+
private final HumanLanguage language;
26+
private final PluralRules cardinal;
27+
private final PluralRules ordinal;
3128

32-
@Override
33-
public HumanLanguage getHumanLanguage() {
34-
return this.language;
35-
}
29+
public DefaultLanguagePluralRulesImpl(HumanLanguage language) {
30+
this.language = language;
31+
this.cardinal = PluralRules.forLocale(language.getLocale(), PluralType.CARDINAL);
32+
this.ordinal = PluralRules.forLocale(language.getLocale(), PluralType.ORDINAL);
33+
}
3634

37-
private PluralRules getPluralRules(NumberType numberType) {
38-
return numberType == NumberType.ORDINAL ? this.ordinal : this.cardinal;
39-
}
40-
41-
// Convert from ICU4j to Grammaticus category
42-
private PluralCategory fromString(String string) {
43-
switch (string) {
44-
case PluralRules.KEYWORD_ZERO: return PluralCategory.ZERO;
45-
case PluralRules.KEYWORD_ONE: return PluralCategory.ONE;
46-
case PluralRules.KEYWORD_TWO: return PluralCategory.TWO;
47-
case PluralRules.KEYWORD_FEW: return PluralCategory.FEW;
48-
case PluralRules.KEYWORD_MANY: return PluralCategory.MANY;
49-
case PluralRules.KEYWORD_OTHER: return PluralCategory.OTHER;
50-
}
51-
return PluralCategory.OTHER;
52-
}
53-
54-
@Override
55-
public PluralCategory getPluralCategory(Number value, NumberType numberType) {
56-
return fromString(getPluralRules(numberType).select(value != null ? value.doubleValue() : 0));
57-
}
35+
@Override
36+
public HumanLanguage getHumanLanguage() {
37+
return this.language;
38+
}
5839

59-
@Override
60-
public Set<PluralCategory> getSupportedCategories(NumberType numberType) {
61-
return getPluralRules(numberType).getKeywords().stream().map(a->fromString(a)).collect(Collectors.toSet());
62-
}
63-
64-
static LanguagePluralRules forLanguage(HumanLanguage language) {
65-
return RULES_CACHE.getUnchecked(language);
66-
}
40+
private PluralRules getPluralRules(NumberType numberType) {
41+
return numberType == NumberType.ORDINAL ? this.ordinal : this.cardinal;
42+
}
6743

68-
// Have a default cache so we don't load it all the time for every language
69-
private static LoadingCache<HumanLanguage, LanguagePluralRules> RULES_CACHE =
70-
CacheBuilder.newBuilder().concurrencyLevel(1).build(CacheLoader.from((l)->new DefaultLanguagePluralRulesImpl(l)));
44+
// Convert from ICU4j to Grammaticus category
45+
private PluralCategory fromString(String string) {
46+
switch (string) {
47+
case PluralRules.KEYWORD_ZERO: return PluralCategory.ZERO;
48+
case PluralRules.KEYWORD_ONE: return PluralCategory.ONE;
49+
case PluralRules.KEYWORD_TWO: return PluralCategory.TWO;
50+
case PluralRules.KEYWORD_FEW: return PluralCategory.FEW;
51+
case PluralRules.KEYWORD_MANY: return PluralCategory.MANY;
52+
case PluralRules.KEYWORD_OTHER: return PluralCategory.OTHER;
53+
default:
54+
return PluralCategory.OTHER;
55+
}
56+
}
57+
58+
@Override
59+
public PluralCategory getPluralCategory(Number value, NumberType numberType) {
60+
return fromString(getPluralRules(numberType).select(value != null ? value.doubleValue() : 0));
61+
}
62+
63+
@Override
64+
public Set<PluralCategory> getSupportedCategories(NumberType numberType) {
65+
return getPluralRules(numberType).getKeywords().stream().map(a -> fromString(a)).collect(Collectors.toSet());
66+
}
67+
68+
static LanguagePluralRules forLanguage(HumanLanguage language) {
69+
return RULES_CACHE.getUnchecked(language);
70+
}
71+
72+
// Have a default cache so we don't load it all the time for every language
73+
private static final LoadingCache<HumanLanguage, LanguagePluralRules> RULES_CACHE =
74+
CaffeinatedGuava.build(Caffeine.newBuilder(), CacheLoader.from(DefaultLanguagePluralRulesImpl::new));
7175
}

src/main/java/com/force/i18n/grammar/parser/GrammaticalLabelSetLoader.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.force.i18n.grammar.*;
2020
import com.force.i18n.grammar.impl.LanguageDeclensionFactory;
2121
import com.force.i18n.settings.*;
22+
import com.github.benmanes.caffeine.cache.Caffeine;
23+
import com.github.benmanes.caffeine.guava.CaffeinatedGuava;
2224
import com.google.common.cache.*;
2325
import com.google.common.util.concurrent.UncheckedExecutionException;
2426

@@ -153,11 +155,11 @@ public GrammaticalLabelSetLoader(LabelSetLoaderConfig config, boolean useSharedK
153155
this.seedKeyMap = null;
154156
}
155157

156-
this.cache = getCacheBuilder(config).build(getCacheLoader());
158+
this.cache = CaffeinatedGuava.build(getCacheBuilder(config), getCacheLoader());
157159
}
158160

159-
protected CacheBuilder<Object, Object> getCacheBuilder(LabelSetLoaderConfig config) {
160-
CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().initialCapacity(64);
161+
protected Caffeine<Object, Object> getCacheBuilder(LabelSetLoaderConfig config) {
162+
Caffeine<Object, Object> builder = Caffeine.newBuilder().initialCapacity(64);
161163

162164
Duration expiration = config.getCacheExpireAfter();
163165
if (!expiration.isZero() && !expiration.isNegative()) builder.expireAfterAccess(expiration);
@@ -234,7 +236,7 @@ protected GrammaticalLabelSet compute(GrammaticalLabelSetDescriptor desc) throws
234236
* LanguageDictionary.
235237
*
236238
* @param language
237-
* the language
239+
* the language
238240
*/
239241
protected LanguageDictionary createNewDictionary(HumanLanguage language) throws IOException {
240242
// default : using default LanguageDictionary
@@ -244,15 +246,15 @@ protected LanguageDictionary createNewDictionary(HumanLanguage language) throws
244246
/**
245247
* Finalize the dictionary after loading. If need something special (e.g. write some data to a file), override this
246248
* method.
247-
*
248-
*
249+
*
250+
*
249251
* @param dictionary the dictionary to be finalized.
250252
* @return the finalized dictionary.
251253
*/
252254

253255
protected LanguageDictionary finalizeDictionary(LanguageDictionary dictionary) throws IOException {
254256
// default : do nothing
255-
return dictionary;
257+
return dictionary;
256258
}
257259

258260
protected GrammaticalLabelSet loadLabels(GrammaticalLabelSetDescriptor desc) throws IOException {
@@ -261,7 +263,7 @@ protected GrammaticalLabelSet loadLabels(GrammaticalLabelSetDescriptor desc) thr
261263
// dictionaries are always unique for every language because it may use different LanguageDeclension
262264
LanguageDictionaryParser dictParser = new LanguageDictionaryParser(desc, createNewDictionary(lang), this.parentProvider);
263265
LanguageDictionary dictionary = finalizeDictionary(dictParser.getDictionary());
264-
266+
265267
// all standard/end-user languages comes here. Create a parser to read from XML files.
266268
GrammaticalLabelFileParser parser = new GrammaticalLabelFileParser(dictionary, desc, this.parentProvider);
267269

src/test/java/com/force/i18n/grammar/parser/GrammaticalLabelTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ public void testCacheConfig() throws InterruptedException {
543543
HumanLanguage ENGLISH_GB = LanguageProviderFactory.get().getLanguage(LanguageConstants.ENGLISH_GB);
544544
HumanLanguage ENGLISH_AU = LanguageProviderFactory.get().getLanguage(LanguageConstants.ENGLISH_AU);
545545
HumanLanguage FRENCH = LanguageProviderFactory.get().getLanguage(LanguageConstants.FRENCH);
546+
HumanLanguage GERMAN = LanguageProviderFactory.get().getLanguage(LanguageConstants.GERMAN);
546547

547548
GrammaticalLabelSetDescriptor desc = getDescriptor();
548549

@@ -552,6 +553,7 @@ public void testCacheConfig() throws InterruptedException {
552553

553554
GrammaticalLabelSetLoader loader = new GrammaticalLabelSetLoader(config);
554555
loader.getSet(ENGLISH);
556+
555557
Thread.sleep(1);
556558
assertNull(loader.getCache().getIfPresent(desc));
557559

@@ -575,8 +577,13 @@ public void testCacheConfig() throws InterruptedException {
575577
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(ENGLISH_GB)));
576578
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(ENGLISH_AU)));
577579

580+
// Caffeine does not evict prior the threadshold, but after the size crossed
578581
loader.getSet(FRENCH);
579-
assertEquals(3, loader.getCache().size());
582+
assertEquals(4, loader.getCache().size());
580583
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(FRENCH)));
584+
585+
loader.getSet(GERMAN);
586+
assertEquals(4, loader.getCache().size());
587+
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(GERMAN)));
581588
}
582589
}

0 commit comments

Comments
 (0)