/** * Copyright (c) 2017 The JNanoID Authors * Copyright (c) 2017 Aventrix LLC * Copyright (c) 2017 Andrey Sitnik * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package net.gepafin.tendermanagement.model.util; import java.security.SecureRandom; import java.util.Random; public final class NanoIdUtils { /** * NanoIdUtils instances should NOT be constructed in standard programming. * Instead, the class should be used as NanoIdUtils.randomNanoId();. */ private NanoIdUtils() { //Do Nothing } /** * The default random number generator used by this class. * Creates cryptographically strong NanoId Strings. */ public static final SecureRandom DEFAULT_NUMBER_GENERATOR = new SecureRandom(); /** * The default alphabet used by this class. * Creates url-friendly NanoId Strings using 64 unique symbols. */ public static final char[] DEFAULT_ALPHABET = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); /** * The default size used by this class. * Creates NanoId Strings with slightly more unique values than UUID v4. */ public static final int DEFAULT_SIZE = 21; /** * Static factory to retrieve a url-friendly, pseudo randomly generated, NanoId String. * * The generated NanoId String will have 21 symbols. * * The NanoId String is generated using a cryptographically strong pseudo random number * generator. * * @return A randomly generated NanoId String. */ public static String randomNanoId() { return randomNanoId(DEFAULT_NUMBER_GENERATOR, DEFAULT_ALPHABET, DEFAULT_SIZE); } /** * Static factory to retrieve a NanoId String. * * The string is generated using the given random number generator. * * @param random The random number generator. * @param alphabet The symbols used in the NanoId String. * @param size The number of symbols in the NanoId String. * @return A randomly generated NanoId String. */ public static String randomNanoId(final Random random, final char[] alphabet, final int size) { if (random == null) { throw new IllegalArgumentException("random cannot be null."); } if (alphabet == null) { throw new IllegalArgumentException("alphabet cannot be null."); } if (alphabet.length == 0 || alphabet.length >= 256) { throw new IllegalArgumentException("alphabet must contain between 1 and 255 symbols."); } if (size <= 0) { throw new IllegalArgumentException("size must be greater than zero."); } double value = (double) (alphabet.length - 1); final int mask = (2 << (int) Math.floor(Math.log(value) / Math.log(2))) - 1; final int step = (int) Math.ceil(1.6 * mask * size / alphabet.length); final StringBuilder idBuilder = new StringBuilder(); while (true) { final byte[] bytes = new byte[step]; random.nextBytes(bytes); for (int i = 0; i < step; i++) { final int alphabetIndex = bytes[i] & mask; if (alphabetIndex < alphabet.length) { idBuilder.append(alphabet[alphabetIndex]); if (idBuilder.length() == size) { return idBuilder.toString(); } } } } } }