Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
<jbanking.version>4.3.0</jbanking.version>
<junit.version>6.0.3</junit.version>
<libphonenumber.version>9.0.24</libphonenumber.version>
<jspecify.version>1.0.0</jspecify.version>
<error_prone.version>2.42.0</error_prone.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven-compiler-plugin.version>3.15.0</maven-compiler-plugin.version>
Expand Down Expand Up @@ -89,6 +91,20 @@
<version>${libphonenumber.version}</version>
</dependency>

<!-- Annotations for nullability etc. -->
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>${jspecify.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>${error_prone.version}</version>
<scope>provided</scope>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
45 changes: 24 additions & 21 deletions src/main/java/net/datafaker/annotations/FakeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.datafaker.internal.helper.CopyOnWriteMap;
import net.datafaker.transformations.JavaObjectTransformer;
import net.datafaker.transformations.Schema;
import org.jspecify.annotations.Nullable;

import static java.util.Objects.requireNonNull;

Expand All @@ -24,19 +25,22 @@ private FakeResolver(Class<T> clazz) {
this.clazz = clazz;
}

@SuppressWarnings("unchecked")
public static <T> FakeResolver<T> of(Class<T> clazz) {
var fakeFactory = CLASS_2_FAKE_RESOLVER.computeIfAbsent(clazz, k -> new FakeResolver<>(clazz));
return (FakeResolver<T>) fakeFactory;
}

public T generate(Schema<Object, ?> schema) {
@SuppressWarnings("unchecked")
public T generate(@Nullable Schema<Object, ?> schema) {
if (schema == null) {
return generateFromDefaultSchema();
}

return (T) JAVA_OBJECT_TRANSFORMER.apply(clazz, schema);
}

@SuppressWarnings("unchecked")
private T generateFromDefaultSchema() {
Schema<Object, ?> useSchema = DEFAULT_SCHEMA_CACHE.computeIfAbsent(clazz, (__) -> {
FakeForSchema fakeForSchemaAnnotation = checkFakeAnnotation(clazz);
Expand All @@ -46,28 +50,27 @@ private T generateFromDefaultSchema() {
return (T) JAVA_OBJECT_TRANSFORMER.apply(clazz, useSchema);
}

@SuppressWarnings("unchecked")
private Schema<Object, T> getSchema(String pathToSchema) {
if (pathToSchema != null) {
try {
// indexOf(<String>) is faster than indexOf(<char>) since it has jvm intrinsic
final int sharpIndex = pathToSchema.indexOf("#");
final Class<?> classToCall;
final String methodName;
if (sharpIndex >= 0) {
classToCall = Class.forName(pathToSchema.substring(0, sharpIndex));
methodName = pathToSchema.substring(sharpIndex + 1);
} else {
classToCall = this.clazz.getEnclosingClass();
methodName = pathToSchema;
}
Method myStaticMethod = classToCall.getMethod(methodName);
myStaticMethod.setAccessible(true);
return (Schema<Object, T>) myStaticMethod.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
requireNonNull(pathToSchema, "The path to the schema is empty.");

try {
// indexOf(<String>) is faster than indexOf(<char>) since it has jvm intrinsic
final int sharpIndex = pathToSchema.indexOf("#");
final Class<?> classToCall;
final String methodName;
if (sharpIndex >= 0) {
classToCall = Class.forName(pathToSchema.substring(0, sharpIndex));
methodName = pathToSchema.substring(sharpIndex + 1);
} else {
classToCall = this.clazz.getEnclosingClass();
methodName = pathToSchema;
}
} else {
throw new IllegalArgumentException("The path to the schema is empty.");
Method myStaticMethod = classToCall.getMethod(methodName);
myStaticMethod.setAccessible(true);
return (Schema<Object, T>) myStaticMethod.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/datafaker/annotations/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@NullMarked
@CheckReturnValue
package net.datafaker.annotations;

import com.google.errorprone.annotations.CheckReturnValue;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import static net.datafaker.idnumbers.Utils.randomGender;

/**
* Estonian personal identification number ("Isikukood" in estonian)
* Estonian personal identification number ("Isikukood" in Estonian)
* <p>
* The number is 11 digits, with modulus 11 checksum digit.
* There is fixed list of valid first digits to signify gender and birth century
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/datafaker/idnumbers/PolishIdNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.datafaker.providers.base.IdNumber.GenderRequest;
import net.datafaker.providers.base.IdNumber.IdNumberRequest;
import net.datafaker.providers.base.PersonIdNumber;
import org.jspecify.annotations.Nullable;

import java.time.LocalDate;

Expand Down Expand Up @@ -52,7 +53,7 @@ public String get(BaseProviders faker, LocalDate birthDate, Gender requestedGend
return get(faker, birthDate, gender);
}

private static PersonIdNumber.Gender pickGender(BaseProviders faker, Gender requestedGender) {
private static PersonIdNumber.Gender pickGender(BaseProviders faker, @Nullable Gender requestedGender) {
return requestedGender == null ? randomGender(faker) :
switch (requestedGender) {
case ANY -> randomGender(faker);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/datafaker/idnumbers/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@NullMarked
@CheckReturnValue
package net.datafaker.idnumbers;

import com.google.errorprone.annotations.CheckReturnValue;
import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static boolean isCNPJValid(final String cnpj) {

/**
* Return true if the CPF is valid
* A valid CPF is unique and have a algorithm to validate it
* A valid CPF is unique and have an algorithm to validate it
* <p>
* CPF generator could generate a valid or invalid because, sometimes, we need to test a
* registration with invalid number
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/datafaker/idnumbers/pt/br/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@NullMarked
@CheckReturnValue
package net.datafaker.idnumbers.pt.br;

import com.google.errorprone.annotations.CheckReturnValue;
import org.jspecify.annotations.NullMarked;
5 changes: 4 additions & 1 deletion src/main/java/net/datafaker/internal/helper/JavaNames.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.datafaker.internal.helper;

import org.jspecify.annotations.Nullable;

import static java.lang.Character.isLetter;
import static java.lang.Character.toLowerCase;
import static java.lang.Character.toUpperCase;
Expand All @@ -8,7 +10,8 @@
import static net.datafaker.internal.helper.JavaNames.Transform.TO_UPPER;

public class JavaNames {
public static String toJavaNames(String string, boolean isMethod) {
@Nullable
public static String toJavaNames(@Nullable String string, boolean isMethod) {
if (string == null || string.isEmpty()) return string;

int length = string.length();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package net.datafaker.internal.helper;

import org.jspecify.annotations.Nullable;

import java.util.function.Supplier;

import static java.util.Objects.requireNonNull;

public class LazyEvaluated<T> {
@Nullable
private volatile T value;
private final Supplier<T> supplier;

Expand All @@ -18,6 +23,6 @@ public T get() {
}
}
}
return value;
return requireNonNull(value, "Null value not allowed");
}
}
13 changes: 10 additions & 3 deletions src/main/java/net/datafaker/internal/helper/SingletonLocale.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package net.datafaker.internal.helper;

import java.util.HashMap;
import org.jspecify.annotations.Nullable;

import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* This class allows to use {@link java.util.IdentityHashMap} for locales.
* To do that it guarantees one instance of {@link SingletonLocale} per {@link Locale}.
*/
public class SingletonLocale {
private static final Map<Locale, SingletonLocale> LOCALE2SINGLETON_LOCALE = new HashMap<>();
private static final Map<Locale, SingletonLocale> LOCALE2SINGLETON_LOCALE = new ConcurrentHashMap<>();
private final Locale locale;

// Hash code is required for FakerContext where SingletonLocale is a field
Expand All @@ -19,10 +21,15 @@ private SingletonLocale(Locale locale) {
this.locale = locale;
}

public static SingletonLocale get(Locale locale) {
@Nullable
public static SingletonLocale get(@Nullable Locale locale) {
if (locale == null) {
return null;
}
return getRequired(locale);
}

public static SingletonLocale getRequired(Locale locale) {
SingletonLocale res = LOCALE2SINGLETON_LOCALE.get(locale);
if (res != null) {
return res;
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/net/datafaker/internal/helper/WordUtils.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package net.datafaker.internal.helper;

import org.jspecify.annotations.Nullable;

public class WordUtils {

public static String capitalize(String input) {
/**
* @deprecated Use {@link #capitalizeWords(String)} instead -
* it doesn't accept nulls, and doesn't return nulls.
*/
@Deprecated
@Nullable
public static String capitalize(@Nullable String input) {
if (input == null) return null;
return capitalizeWords(input);
}

public static String capitalizeWords(String input) {
if (input.isEmpty()) return input;
final char ch0 = input.charAt(0);
if (Character.isUpperCase(ch0)) return input;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/datafaker/internal/helper/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@NullMarked
@CheckReturnValue
package net.datafaker.internal.helper;

import com.google.errorprone.annotations.CheckReturnValue;
import org.jspecify.annotations.NullMarked;
6 changes: 6 additions & 0 deletions src/main/java/net/datafaker/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@NullMarked
@CheckReturnValue
package net.datafaker;

import com.google.errorprone.annotations.CheckReturnValue;
import org.jspecify.annotations.NullMarked;
4 changes: 2 additions & 2 deletions src/main/java/net/datafaker/providers/base/Animal.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package net.datafaker.providers.base;

import net.datafaker.internal.helper.WordUtils;
import static net.datafaker.internal.helper.WordUtils.capitalizeWords;

/**
* @since 0.8.0
Expand All @@ -20,7 +20,7 @@ public String scientificName() {
}

public String genus() {
return WordUtils.capitalize(faker.resolve("creature.animal.genus"));
return capitalizeWords(faker.resolve("creature.animal.genus"));
}

public String species() {
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/net/datafaker/providers/base/BaseFaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.datafaker.service.FakerContext;
import net.datafaker.service.RandomService;
import net.datafaker.transformations.Schema;
import org.jspecify.annotations.Nullable;

import java.lang.reflect.Method;
import java.net.URL;
Expand Down Expand Up @@ -47,7 +48,7 @@ public BaseFaker(Random random) {
this(Locale.ENGLISH, random);
}

public BaseFaker(Locale locale, Random random) {
public BaseFaker(Locale locale, @Nullable Random random) {
this(locale, new RandomService(random));
}

Expand All @@ -59,7 +60,7 @@ public BaseFaker(FakeValuesService fakeValuesService, FakerContext context) {
this(fakeValuesService, context, EVERY_PROVIDER_ALLOWED);
}

public BaseFaker(FakeValuesService fakeValuesService, FakerContext context, Predicate<Class<?>> whiteListPredicate) {
public BaseFaker(FakeValuesService fakeValuesService, FakerContext context, @Nullable Predicate<Class<?>> whiteListPredicate) {
this.fakeValuesService = fakeValuesService;
this.context = context;
this.whiteListPredicate = whiteListPredicate == null ? EVERY_PROVIDER_ALLOWED : whiteListPredicate;
Expand Down Expand Up @@ -430,7 +431,8 @@ public final <B extends ProviderRegistration> B getFaker() {
return (B) this;
}

public static Method getMethod(AbstractProvider<?> ap, String methodName) {
@Nullable
public static Method getMethod(@Nullable AbstractProvider<?> ap, String methodName) {
return ap == null ? null : ObjectMethods.getMethodByName(ap, methodName);
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/net/datafaker/providers/base/Compass.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.datafaker.providers.base;

import org.jspecify.annotations.Nullable;

/**
* @since 1.7.0
*/
Expand All @@ -18,6 +20,7 @@ public enum CompassPoint {
}
}

@Nullable
private CompassPoint compassPoint;

protected Compass(BaseProviders faker) {
Expand Down
Loading