io.github.serpro69.kfaker.RandomService.kt Maven / Gradle / Ivy
package io.github.serpro69.kfaker
import com.ibm.icu.text.UnicodeSet
import com.ibm.icu.util.LocaleData
import com.ibm.icu.util.ULocale
import java.util.*
import kotlin.experimental.and
import kotlin.experimental.or
import kotlin.random.asKotlinRandom
/**
* Wrapper around [Random] that also contains some additional functions not covered by [Random].
*
* If two instances of this [RandomService] are created with the same seed,
* and the same sequence of method calls is made for each,
* then they will generate and return identical sequences of values.
*
* Instances of [RandomService] are not cryptographically secure by default.
* Consider passing [java.security.SecureRandom] to the constructor of this [RandomService]
* to get a cryptographically secure pseudo-random generator.
*/
internal class RandomService internal constructor(override val config: FakerConfig) : IRandom {
private val random = config.random
private val alphabeticLowerCharset = ('a'..'z')
private val alphabeticUpperCharset = ('A'..'Z')
private val numericCharset = ('0'..'9')
override fun nextInt() = random.nextInt()
override fun nextInt(bound: Int) = random.nextInt(bound)
override fun nextInt(intRange: IntRange): Int {
val lowerBound = requireNotNull(intRange.minOrNull())
val upperBound = requireNotNull(intRange.maxOrNull())
return nextInt(lowerBound, upperBound)
}
override fun nextInt(min: Int, max: Int) = random.nextInt(max - min + 1) + min
override fun randomValue(list: List) = list[nextInt(list.size)]
override fun randomValue(array: Array) = array[nextInt(array.size)]
override fun nextLetter(upper: Boolean): Char {
val source = if (upper) alphabeticUpperCharset else alphabeticLowerCharset
return source.random(config.random.asKotlinRandom())
}
override fun randomString(length: Int, numericalChars: Boolean): String {
if (length < 1) return ""
val charset = if (numericalChars) {
alphabeticLowerCharset + alphabeticUpperCharset + numericCharset
} else alphabeticLowerCharset + alphabeticUpperCharset
return (1..length)
.map { charset.random(this.random.asKotlinRandom()) }
.joinToString("")
}
override fun nextBoolean() = random.nextBoolean()
override fun nextLong() = random.nextLong()
override fun nextLong(bound: Long): Long {
return if (bound > 0) {
var value: Long
do {
val bits = (nextLong().shl(1)).shr(1)
value = bits % bound
} while (bits - value + (bound - 1) < 0L)
value
} else throw IllegalArgumentException("Bound bound must be greater than 0")
}
override fun nextFloat() = random.nextFloat()
override fun nextDouble() = random.nextDouble()
override fun nextChar() = nextInt().toChar()
@Deprecated(
message = "This function is deprecated and will be removed in future releases.\n" +
"Note that default value for 'length' param has changed from '100' to '24' in the new 'randomString' function.",
replaceWith = ReplaceWith("randomString"),
level = DeprecationLevel.WARNING
)
override fun nextString(
length: Int,
locale: Locale,
auxiliaryChars: Boolean,
numericalChars: Boolean
): String = randomString(length, locale, auxiliaryChars, numericalChars)
override fun randomString(
length: Int,
locale: Locale,
indexChars: Boolean,
auxiliaryChars: Boolean,
punctuationChars: Boolean,
numericalChars: Boolean,
): String {
if (length < 1) return "" // base case
val localeData = LocaleData.getInstance(ULocale.forLocale(locale))
val mainChars = localeData.getExemplarSet(UnicodeSet.MIN_VALUE, LocaleData.ES_STANDARD)
.ranges()
.flatMap { (it.codepoint..it.codepointEnd).map { code -> Char(code) } }
val auxChars = if (auxiliaryChars) {
localeData.getExemplarSet(UnicodeSet.MIN_VALUE, LocaleData.ES_AUXILIARY)
.ranges()
.flatMap { (it.codepoint..it.codepointEnd).map { code -> Char(code) } }
} else emptyList()
val idxChars = if (indexChars) {
localeData.getExemplarSet(UnicodeSet.MIN_VALUE, LocaleData.ES_INDEX)
.ranges()
.flatMap { (it.codepoint..it.codepointEnd).map { code -> Char(code) } }
} else emptyList()
val punctChars = if (punctuationChars) {
localeData.getExemplarSet(UnicodeSet.MIN_VALUE, LocaleData.ES_PUNCTUATION)
.ranges()
.flatMap { (it.codepoint..it.codepointEnd).map { code -> Char(code) } }
} else emptyList()
val numChars = if (numericalChars) numericCharset else emptyList()
val chars = (mainChars + auxChars + idxChars + punctChars + numChars)
return List(length) { chars.random(random.asKotlinRandom()) }.joinToString("")
}
override fun randomString(
min: Int,
max: Int,
locale: Locale,
indexChars: Boolean,
auxiliaryChars: Boolean,
punctuationChars: Boolean,
numericalChars: Boolean,
): String {
val len = nextInt(min, max)
return randomString(
len,
locale,
indexChars = indexChars,
auxiliaryChars = auxiliaryChars,
punctuationChars = punctuationChars,
numericalChars = numericalChars,
)
}
/**
* Returns a randomly selected enum entry of type [E].
*/
inline fun > nextEnum(): E {
val x: Int = nextInt(enumValues().size)
return enumValues()[x]
}
override fun > nextEnum(enum: Class): E {
return randomValue(enum.enumConstants)
}
override fun > nextEnum(values: Array): E {
return randomValue(values)
}
override tailrec fun > nextEnum(enum: Class, predicate: (E) -> Boolean): E {
val enumClass = nextEnum(enum)
return if (predicate(enumClass)) enumClass else nextEnum(enum, predicate)
}
/**
* Returns a randomly selected enum entry of type [E] excluding a particular enum class by its name.
*/
inline fun > nextEnum(excludeName: String): E {
do {
val e: E = nextEnum()
if (e.name.lowercase() != excludeName.lowercase()) {
return e
}
} while (true)
}
override fun nextUUID(): String {
val randomBytes = ByteArray(16)
random.nextBytes(randomBytes)
randomBytes[6] = randomBytes[6] and 0x0f // clear version
randomBytes[6] = randomBytes[6] or 0x40 // set to version
randomBytes[8] = randomBytes[8] and 0x3f // clear variant
randomBytes[8] = randomBytes[8] or 0x80.toByte() // set to IETF variant
return UUID.nameUUIDFromBytes(randomBytes).toString()
}
override fun randomSublist(list: List, size: Int, shuffled: Boolean): List {
val (from, to) = list.randomFromToIndices(size)
return list.ifEmpty { emptyList() }
.let { if (shuffled) it.shuffled(random) else it }
.subList(from, to)
}
override fun randomSublist(list: List, sizeRange: IntRange, shuffled: Boolean): List {
return randomSublist(list, nextInt(sizeRange), shuffled)
}
override fun randomSubset(set: Set, size: Int, shuffled: Boolean): Set {
val (from, to) = set.randomFromToIndices(size)
return set.ifEmpty { emptyList() }
.let { if (shuffled) it.shuffled(random) else it }
.mapIndexedNotNull { i, v -> if (i in from until to) v else null }
.toSet()
}
override fun randomSubset(set: Set, sizeRange: IntRange, shuffled: Boolean): Set {
return randomSubset(set, nextInt(sizeRange), shuffled)
}
private fun Collection.randomFromToIndices(s: Int): Pair {
val fromIndex = if (s > 0) {
(0..size - s).random(random.asKotlinRandom())
} else {
(indices).random(random.asKotlinRandom())
}
val toIndex = if (s > 0) {
fromIndex + s
} else {
(fromIndex..size).random(random.asKotlinRandom())
}
return fromIndex to toIndex
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy