Skip to content

Commit

Permalink
Cache suppliers for java transformers, improve hashing (#810) (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
snuyanzin committed May 2, 2023
1 parent 9a32f4b commit 1e7544b
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 45 deletions.
12 changes: 11 additions & 1 deletion src/main/java/net/datafaker/annotations/FakeResolver.java
Expand Up @@ -12,15 +12,25 @@
public class FakeResolver<T> {

private static final JavaObjectTransformer JAVA_OBJECT_TRANSFORMER = new JavaObjectTransformer();
private static final Map<Class<?>, FakeResolver<?>> CLASS_2_FAKE_RESOLVER = new IdentityHashMap<>();

private static final Map<Class<?>, Schema<Object, ?>> DEFAULT_SCHEMA_CACHE = new IdentityHashMap<>();

private final Class<T> clazz;

public FakeResolver(Class<T> clazz) {
private FakeResolver(Class<T> clazz) {
this.clazz = clazz;
}

public static <T> FakeResolver<T> of(Class<T> clazz) {
FakeResolver<?> fakeFactory = CLASS_2_FAKE_RESOLVER.get(clazz);
if (fakeFactory == null) {
fakeFactory = new FakeResolver<>(clazz);
CLASS_2_FAKE_RESOLVER.put(clazz, fakeFactory);
}
return (FakeResolver<T>) fakeFactory;
}

public T generate(Schema<Object, ?> schema) {
if (Objects.isNull(schema)) {
return generateFromDefaultSchema();
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/net/datafaker/providers/base/AbstractProvider.java
@@ -1,5 +1,6 @@
package net.datafaker.providers.base;

import java.util.Objects;
import java.util.function.Supplier;

public class AbstractProvider<T extends ProviderRegistration> {
Expand All @@ -20,4 +21,18 @@ protected String resolve(String key) {
protected String resolve(String key, Supplier<String> message) {
return faker.fakeValuesService().resolve(key, this, faker, message, faker.getContext());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AbstractProvider<?>)) return false;

AbstractProvider<?> that = (AbstractProvider<?>) o;
return Objects.equals(faker, that.faker);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}
4 changes: 2 additions & 2 deletions src/main/java/net/datafaker/providers/base/BaseFaker.java
Expand Up @@ -337,12 +337,12 @@ public void addPath(Locale locale, Path path) {
}

public static <T> T populate(Class<T> clazz) {
final FakeResolver<T> fakeFactory = new FakeResolver<>(clazz);
final FakeResolver<T> fakeFactory = FakeResolver.of(clazz);
return fakeFactory.generate(null);
}

public static <T> T populate(Class<T> clazz, Schema<Object, ?> schema) {
final FakeResolver<T> fakeFactory = new FakeResolver<>(clazz);
final FakeResolver<T> fakeFactory = FakeResolver.of(clazz);
return fakeFactory.generate(schema);
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/net/datafaker/service/FakeValuesService.java
Expand Up @@ -563,7 +563,7 @@ protected String resolveExpression(String expression, Object current, ProviderRe
continue;
}
final RegExpContext regExpContext = RegExpContext.of(expr, current, root, context);
Supplier<?> val = map.get(regExpContext);
final Supplier<?> val = map.get(regExpContext);
final Object resolved;
if (val != null) {
resolved = val.get();
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/net/datafaker/service/RegExpContext.java
Expand Up @@ -53,8 +53,6 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
int result = exp != null ? exp.hashCode() : 0;
result = 31 * result + (current != null ? current.hashCode() : 0);
result = 31 * result + (root != null ? root.hashCode() : 0);
result = 31 * result + (context != null ? context.hashCode() : 0);
return result;
}
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/net/datafaker/transformations/CompositeField.java
Expand Up @@ -2,6 +2,8 @@

import net.datafaker.providers.base.AbstractProvider;

import java.util.Objects;

public class CompositeField<MyObject extends AbstractProvider<?>, MyType> extends Schema<MyObject, MyType> implements Field<MyObject, MyType> {
private final String name;

Expand All @@ -19,4 +21,21 @@ public String getName() {
public MyType transform(MyObject input) {
return null;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CompositeField<?, ?>)) return false;
if (!super.equals(o)) return false;

CompositeField<?, ?> that = (CompositeField<?, ?>) o;
return Objects.equals(name, that.name);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}
Expand Up @@ -4,46 +4,67 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class JavaObjectTransformer implements Transformer<Object, Object> {
private static final Map<Class<?>, Object> CLASS2RESULT = new IdentityHashMap<>();
private static final Map<Schema<Object, ?>, Consumer<Schema<Object, ?>>> SCHEMA2CONSUMER = new IdentityHashMap<>();

@Override
public Object apply(Object input, Schema<Object, ?> schema) {
Class clazz;
Class<?> clazz;
Object result;
if (input instanceof Class) {
clazz = (Class) input;
try {
result = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
clazz = (Class<?>) input;
result = CLASS2RESULT.get(clazz);
if (result == null) {
try {
result = clazz.newInstance();
CLASS2RESULT.put(clazz, result);
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
} else {
clazz = input.getClass();
result = input;
}
Field<Object, ?>[] fields = schema.getFields();
Map<String, java.lang.reflect.Field> name2ClassField = Stream.of(clazz.getDeclaredFields()).collect(
Collectors.toMap(java.lang.reflect.Field::getName, Function.identity()));
try {
for (Field<Object, ?> f: fields) {
name2ClassField.get(f.getName()).setAccessible(true);
name2ClassField.get(f.getName()).set(result, f.transform(result));
final Object classObject = result;
Consumer<Schema<Object, ?>> consumer = SCHEMA2CONSUMER.get(schema);
if (consumer == null) {
final Field<Object, ?>[] fields = schema.getFields();
final Map<String, java.lang.reflect.Field> name2ClassField = Stream.of(clazz.getDeclaredFields()).collect(
Collectors.toMap(java.lang.reflect.Field::getName, Function.identity()));
final java.lang.reflect.Field[] rFields = new java.lang.reflect.Field[fields.length];
for (int i = 0; i < fields.length; i++) {
rFields[i] = name2ClassField.get(fields[i].getName());
rFields[i].setAccessible(true);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
consumer = objectSchema -> {
try {
for (int i = 0; i < fields.length; i++) {
rFields[i].set(classObject, fields[i].transform(classObject));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
SCHEMA2CONSUMER.put(schema, consumer);
}
consumer.accept(schema);
return result;
}

@Override
public Collection<Object> generate(Iterable<Object> input, Schema<Object, ?> schema) {
Collection<Object> collection;
if (input instanceof FakeSequence) {
if (((FakeSequence) input).isInfinite()) {
if (((FakeSequence<?>) input).isInfinite()) {
throw new IllegalArgumentException("Should be finite size");
}
collection = new ArrayList<>(((FakeSequence<Object>) input).get());
Expand Down
23 changes: 0 additions & 23 deletions src/main/java/net/datafaker/transformations/Schema.java
@@ -1,10 +1,6 @@
package net.datafaker.transformations;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Schema<IN, OUT> {
private final Field<IN, OUT>[] fields;

Expand All @@ -20,23 +16,4 @@ public Field<IN, OUT>[] getFields() {
public static <IN, OUT> Schema<IN, OUT> of(Field<IN, OUT>... fields) {
return new Schema<>(fields);
}

public static class SchemaBuilder<IN, OUT> {
private final List<Field<IN, OUT>> fieldList = new ArrayList<>();

public SchemaBuilder<IN, OUT> of(SimpleField<IN, OUT> field) {
fieldList.add(field);
return this;
}

@SafeVarargs
public final SchemaBuilder<IN, OUT> of(Field<IN, OUT>... fields) {
fieldList.addAll(Arrays.asList(fields));
return this;
}

public Schema<IN, OUT> build() {
return new Schema<>(fieldList.toArray(new Field[0]));
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/net/datafaker/transformations/SimpleField.java
@@ -1,6 +1,7 @@
package net.datafaker.transformations;


import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

Expand Down Expand Up @@ -49,4 +50,20 @@ public Function<MyObject, MyType> getTransform() {
public Supplier<MyType> getSupplier() {
return supplier;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SimpleField<?, ?>)) return false;

SimpleField<?, ?> that = (SimpleField<?, ?>) o;
if (!Objects.equals(name, that.name)) return false;
if (!Objects.equals(transform, that.transform)) return false;
return Objects.equals(supplier, that.supplier);
}

@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
}

0 comments on commit 1e7544b

Please sign in to comment.