Skip to content

Commit 2de6ba3

Browse files
authored
Merge pull request #1 from jpeffer/feature/merge-from-datafaker-origin
Feature/merge from datafaker origin
2 parents 8c03063 + 2e44d6a commit 2de6ba3

File tree

3 files changed

+78
-21
lines changed

3 files changed

+78
-21
lines changed

src/main/java/net/datafaker/Faker.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.List;
1010
import java.util.Locale;
1111
import java.util.Map;
12+
import java.util.concurrent.Callable;
1213
import java.util.function.Supplier;
1314

1415
/**
@@ -96,6 +97,21 @@ public Locale getLocale() {
9697
? Locale.ROOT : fakeValuesService.getLocalesChain().get(0);
9798
}
9899

100+
public <T> T doWith(Callable<T> callable, Locale locale) {
101+
final Locale current = fakeValuesService.getCurrentLocale();
102+
T result;
103+
try {
104+
fakeValuesService.setCurrentLocale(locale);
105+
result = callable.call();
106+
return result;
107+
} catch (Throwable t) {
108+
throw new RuntimeException(t);
109+
} finally {
110+
fakeValuesService.setCurrentLocale(current);
111+
}
112+
113+
}
114+
99115
/**
100116
* Returns a string with the '#' characters in the parameter replaced with random digits between 0-9 inclusive.
101117
* <p>

src/main/java/net/datafaker/service/FakeValuesService.java

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,24 @@ public class FakeValuesService {
3939
private static final Logger LOG = Logger.getLogger("faker");
4040
private static final Map<Locale, FakeValuesInterface> FAKE_VALUES_CACHE = new HashMap<>();
4141

42-
private final Map<Locale, FakeValuesInterface> fakeValuesInterfaceMap = new HashMap<>();
42+
private static final Map<Locale, FakeValuesInterface> fakeValuesInterfaceMap = new HashMap<>();
43+
private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
4344
private final RandomService randomService;
45+
private Locale currentLocale;
4446

45-
private final List<Locale> localesChain;
47+
private final Map<Locale, List<Locale>> locale2localesChain;
4648

47-
private final Map<Class<?>, Map<String, Collection<Method>>> class2methodsCache = new IdentityHashMap<>();
48-
private final Map<Class<?>, Constructor<?>> class2constructorCache = new IdentityHashMap<>();
49+
private static final Map<Class<?>, Map<String, Collection<Method>>> class2methodsCache = new IdentityHashMap<>();
50+
private static final Map<Class<?>, Constructor<?>> class2constructorCache = new IdentityHashMap<>();
4951
private final Map<String, Generex> expression2generex = new WeakHashMap<>();
50-
private final Map<String, String> key2Expression = new WeakHashMap<>();
52+
private final Map<Locale, Map<String, String>> key2Expression = new WeakHashMap<>();
5153
private final Map<String, String[]> args2splittedArgs = new WeakHashMap<>();
5254
private final Map<String, String[]> key2splittedKey = new WeakHashMap<>();
5355

54-
private final Map<String, Object> key2fetchedObject = new WeakHashMap<>();
56+
private final Map<Locale, Map<String, Object>> key2fetchedObject = new WeakHashMap<>();
5557
private final Map<String, String> name2yaml = new WeakHashMap<>();
5658

57-
private final Map<Class<?>, Map<String, Map<String[], MethodAndCoercedArgs>>> mapOfMethodAndCoercedArgs = new IdentityHashMap<>();
59+
private static final Map<Class<?>, Map<String, Map<String[], MethodAndCoercedArgs>>> mapOfMethodAndCoercedArgs = new IdentityHashMap<>();
5860

5961
private static final Map<String, List<String>> EXPRESSION_2_SPLITTED = new WeakHashMap<>();
6062

@@ -79,16 +81,23 @@ public FakeValuesService(Locale locale, RandomService randomService) {
7981
throw new IllegalArgumentException("locale is required");
8082
}
8183
this.randomService = randomService;
82-
locale = normalizeLocale(locale);
84+
this.locale2localesChain = new HashMap<>();
85+
setCurrentLocale(locale);
86+
}
8387

84-
localesChain = localeChain(locale);
85-
for (final Locale l : localesChain) {
88+
public void setCurrentLocale(Locale locale) {
89+
currentLocale = normalizeLocale(locale);
90+
if (locale2localesChain.containsKey(currentLocale)) {
91+
return;
92+
}
93+
locale2localesChain.put(currentLocale, localeChain(currentLocale));
94+
for (final Locale l : locale2localesChain.get(currentLocale)) {
8695
fakeValuesInterfaceMap.computeIfAbsent(l, this::getCachedFakeValue);
8796
}
8897
}
8998

9099
private FakeValuesInterface getCachedFakeValue(Locale locale) {
91-
if (Locale.ENGLISH.equals(locale)) {
100+
if (DEFAULT_LOCALE.equals(locale)) {
92101
return FakeValuesGrouping.getEnglishFakeValueGrouping();
93102
}
94103
return FAKE_VALUES_CACHE.computeIfAbsent(locale, FakeValues::new);
@@ -126,18 +135,18 @@ public void addPath(Locale locale, Path path) {
126135
* @return a list of {@link Locale} instances
127136
*/
128137
protected List<Locale> localeChain(Locale from) {
129-
if (Locale.ENGLISH.equals(from)) {
130-
return Collections.singletonList(Locale.ENGLISH);
138+
if (DEFAULT_LOCALE.equals(from)) {
139+
return Collections.singletonList(DEFAULT_LOCALE);
131140
}
132141

133142
final Locale normalized = normalizeLocale(from);
134143

135144
final List<Locale> chain = new ArrayList<>(3);
136145
chain.add(normalized);
137-
if (!"".equals(normalized.getCountry()) && !Locale.ENGLISH.getLanguage().equals(normalized.getLanguage())) {
146+
if (!"".equals(normalized.getCountry()) && !DEFAULT_LOCALE.getLanguage().equals(normalized.getLanguage())) {
138147
chain.add(new Locale(normalized.getLanguage()));
139148
}
140-
chain.add(Locale.ENGLISH); // default
149+
chain.add(DEFAULT_LOCALE); // default
141150
return chain;
142151
}
143152

@@ -157,7 +166,11 @@ private Locale normalizeLocale(Locale locale) {
157166
}
158167

159168
public List<Locale> getLocalesChain() {
160-
return localesChain;
169+
return locale2localesChain.get(currentLocale);
170+
}
171+
172+
public Locale getCurrentLocale() {
173+
return currentLocale;
161174
}
162175

163176
/**
@@ -219,13 +232,23 @@ public String safeFetch(String key, String defaultIfNull) {
219232
* dot. E.g. name.first_name
220233
*/
221234
public Object fetchObject(String key) {
222-
Object result = key2fetchedObject.get(key);
235+
Object result = null;
236+
for (Locale locale: locale2localesChain.get(currentLocale)) {
237+
// exclude default locale from cache checks
238+
if (locale.equals(DEFAULT_LOCALE) && locale2localesChain.get(currentLocale).size() > 1) {
239+
continue;
240+
}
241+
if (key2fetchedObject.get(locale) != null && (result = key2fetchedObject.get(locale).get(key)) != null) {
242+
break;
243+
}
244+
}
223245
if (result != null) {
224246
return result;
225247
}
226248

227249
String[] path = split(key);
228-
for (Locale locale : localesChain) {
250+
Locale local2Add = null;
251+
for (Locale locale : locale2localesChain.get(currentLocale)) {
229252
Object currentValue = fakeValuesInterfaceMap.get(locale);
230253
for (int p = 0; currentValue != null && p < path.length; p++) {
231254
String currentPath = path[p];
@@ -237,10 +260,14 @@ public Object fetchObject(String key) {
237260
}
238261
result = currentValue;
239262
if (result != null) {
263+
local2Add = locale;
264+
key2fetchedObject.putIfAbsent(local2Add, new HashMap<>());
240265
break;
241266
}
242267
}
243-
key2fetchedObject.put(key, result);
268+
if (local2Add != null) {
269+
key2fetchedObject.get(local2Add).put(key, result);
270+
}
244271
return result;
245272
}
246273

@@ -417,11 +444,12 @@ public String resolve(String key, Object current, Faker root) {
417444
* #{Person.hello_someone} will result in a method call to person.helloSomeone();
418445
*/
419446
public String resolve(String key, Object current, Faker root, Supplier<String> exceptionMessage) {
420-
String expression = root == null ? key2Expression.get(key) : null;
447+
String expression = root == null ? key2Expression.get(currentLocale).get(key) : null;
421448
if (expression == null) {
422449
expression = safeFetch(key, null);
423450
if (root == null) {
424-
key2Expression.put(key, expression);
451+
key2Expression.putIfAbsent(currentLocale, new HashMap<>());
452+
key2Expression.get(currentLocale).put(key, expression);
425453
}
426454
}
427455

src/test/java/net/datafaker/FakerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.Collections;
99
import java.util.Locale;
10+
import java.util.concurrent.Callable;
1011

1112
import static org.assertj.core.api.Assertions.assertThat;
1213
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -271,4 +272,16 @@ void fakerInstanceCanBeAcquiredViaUtilityMethods() {
271272
assertThat(Faker.instance(1)).isInstanceOf(Faker.class);
272273
assertThat(Faker.instance(Locale.CHINA, 2)).isInstanceOf(Faker.class);
273274
}
275+
276+
@Test
277+
void differentLocalesTest() {
278+
Callable<String> stringCallable = () -> faker.name().firstName();
279+
faker.name().firstName();
280+
faker.doWith(stringCallable, new Locale("ru_RU"));
281+
faker.doWith(stringCallable, Locale.GERMAN);
282+
faker.doWith(stringCallable, Locale.SIMPLIFIED_CHINESE);
283+
for (int i = 0; i < 10; i++) {
284+
assertThat(faker.doWith(stringCallable ,new Locale("ru_RU"))).matches("[а-яА-ЯЁё ]+");
285+
}
286+
}
274287
}

0 commit comments

Comments
 (0)