diff --git a/README.md b/README.md
index d9f51d3..1c7bd26 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,24 @@
# ULID Creator
-A Java library for generating and handling ULIDs - _Universally Unique Lexicographically Sortable Identifiers_.
+A Java library for generating ULIDs.
How to Use
------------------------------------------------------
-Create a ULID:
+Create a ULID as GUID:
```java
-String ulid = UlidCreator.getUlid();
+UUID ulid = UlidCreator.getUlid();
```
-Create a ULID as GUID object:
+Create a ULID string:
```java
-UUID ulid = UlidCreator.getGuid();
+String ulid = UlidCreator.getUlidString();
```
+
### Maven dependency
Add these lines to your `pom.xml`.
@@ -27,7 +28,7 @@ Add these lines to your `pom.xml`.
com.github.f4b6a3
ulid-creator
- 1.0.2
+ 1.1.0
```
See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator) and [mvnrepository.com](https://mvnrepository.com/artifact/com.github.f4b6a3/ulid-creator).
@@ -35,15 +36,15 @@ See more options in [maven.org](https://search.maven.org/artifact/com.github.f4b
Implementation
------------------------------------------------------
-### ULID
+### ULID string
-The ULID is a unique and sortable 26 char sequence. See the [ULID specification](https://github.com/ulid/spec) for more information.
+The ULID is a 26 char sequence. See the [ULID specification](https://github.com/ulid/spec) for more information.
See the section on GUIDs to know how the 128 bits are generated in this library.
```java
// ULIDs
-String ulid = UlidCreator.getUlid();
+String ulid = UlidCreator.getUlidString();
```
Examples of ULIDs:
@@ -71,17 +72,17 @@ Examples of ULIDs:
milli randomness
```
-### GUID
+### Ulid-based GUID
The GUIDs in this library are based on the [ULID specification](https://github.com/ulid/spec). The first 48 bits represent the count of milliseconds since Unix Epoch, 1 January 1970. The remaining 60 bits are generated by a secure random number generator.
Every time the timestamp changes the random part is reset to a new random value. If the current timestamp is equal to the previous one, the random bits are incremented by 1.
-The default random number generator is `SecureRandom`, but it's possible to use any RNG that extends `Random`.
+The default random number generator is `java.security.SecureRandom`, but it's possible to use any RNG that extends `java.util.Random`.
```java
// GUID based on ULID spec
-UUID guid = UlidCreator.getGuid();
+UUID ulid = UlidCreator.getUlid();
```
Examples of GUIDs based on ULID spec:
@@ -109,38 +110,28 @@ Examples of GUIDs based on ULID spec:
millisecs randomness
```
-#### How use the `GuidCreator` directly
+#### How use the `UlidBasedGuidCreator` directly
-These are some examples of using the `GuidCreator` to create ULIDs:
+These are some examples of using the `UlidBasedGuidCreator` to create ULIDs strings:
```java
-
-// with fixed timestamp strategy (for test cases)
-String ulid = UlidCreator.getGuidCreator()
- .withTimestampStrategy(new FixedTimestampStretegy())
- .createUlid();
// with your custom timestamp strategy
-String ulid = UlidCreator.getGuidCreator()
- .withTimestampStrategy(new MyCustomTimestampStrategy())
- .createUlid();
+TimestampStrategy customStrategy = new CustomTimestampStrategy();
+String ulid = UlidCreator.getUlidBasedGuidCreator()
+ .withTimestampStrategy(customStrategy)
+ .createString();
-// with your custom random number generator
-String ulid = UlidCreator.getGuidCreator()
- .withRandomGenerator(new MyCustomRandom())
- .createUlid();
-
-// with fast random generator (Xorshift128Plus with salt)
-int salt = (int) FingerprintUtil.getFingerprint();
-Random random = new Xorshift128PlusRandom(salt);
-String ulid = UlidCreator.getGuidCreator()
+// with `java.util.Random` number generator
+Random random = new Random();
+String ulid = UlidCreator.getUlidBasedGuidCreator()
.withRandomGenerator(random)
- .createUlid();
+ .createString();
// with fast random generator (the same as above)
-String ulid = UlidCreator.getGuidCreator()
+String ulid = UlidCreator.getUlidBasedGuidCreator()
.withFastRandomGenerator()
- .createUlid();
+ .createString();
```
diff --git a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java
index 7e08321..2f4934a 100644
--- a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java
+++ b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java
@@ -26,8 +26,8 @@ package com.github.f4b6a3.ulid;
import java.util.UUID;
+import com.github.f4b6a3.commons.random.Xorshift128PlusRandom;
import com.github.f4b6a3.ulid.creator.UlidBasedGuidCreator;
-import com.github.f4b6a3.ulid.exception.UlidCreatorException;
/**
* A factory for Universally Unique Lexicographically Sortable Identifiers.
@@ -40,61 +40,71 @@ public class UlidCreator {
}
/**
- * Returns ULID as GUID object.
+ * Returns a ULID as GUID.
*
- * @return a GUID
+ * The random component is generated by a secure random number generator:
+ * {@link java.security.SecureRandom}.
+ *
+ * @return a UUID
*/
public static UUID getUlid() {
- return GuidCreatorLazyHolder.INSTANCE.create();
+ return UlidBasedGuidCreatorHolder.INSTANCE.create();
}
/**
- * Returns fast ULID as GUID object.
+ * Returns a ULID string.
*
- * @return a GUID
- */
- public static UUID getFastUlid() {
- return FastGuidCreatorLazyHolder.INSTANCE.create();
- }
-
- /**
- * Returns a ULID.
+ * The returning string is encoded to Crockford's base32.
+ *
+ * The random component is generated by a secure random number generator:
+ * {@link java.security.SecureRandom}.
*
* @return a ULID
*/
public static String getUlidString() {
- return GuidCreatorLazyHolder.INSTANCE.createString();
+ return UlidBasedGuidCreatorHolder.INSTANCE.createString();
}
/**
- * Returns a fast ULID.
+ * Returns a ULID as GUID.
+ *
+ * The random component is generated by a fast random number generator:
+ * {@link Xorshift128PlusRandom}.
+ *
+ * @return a UUID
+ */
+ public static UUID getFastUlid() {
+ return FastUlidBasedGuidCreatorHolder.INSTANCE.create();
+ }
+
+ /**
+ * Returns a fast ULID string.
+ *
+ * The returning string is encoded to Crockford's base32.
+ *
+ * The random component is generated by a fast random number generator:
+ * {@link Xorshift128PlusRandom}.
*
* @return a ULID
*/
public static String getFastUlidString() {
- return FastGuidCreatorLazyHolder.INSTANCE.createString();
+ return FastUlidBasedGuidCreatorHolder.INSTANCE.createString();
}
/**
* Return a GUID creator for direct use.
*
- * This library uses the {@link UlidBasedGuidCreator} internally to generate
- * ULIDs.
- *
- * The {@link UlidBasedGuidCreator} throws a {@link UlidCreatorException} when
- * too many values are requested in the same millisecond.
- *
* @return a {@link UlidBasedGuidCreator}
*/
public static UlidBasedGuidCreator getUlidBasedCreator() {
return new UlidBasedGuidCreator();
}
- private static class GuidCreatorLazyHolder {
+ private static class UlidBasedGuidCreatorHolder {
static final UlidBasedGuidCreator INSTANCE = getUlidBasedCreator();
}
- private static class FastGuidCreatorLazyHolder {
+ private static class FastUlidBasedGuidCreatorHolder {
static final UlidBasedGuidCreator INSTANCE = getUlidBasedCreator().withFastRandomGenerator();
}
}
diff --git a/src/main/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreator.java b/src/main/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreator.java
index febe3e1..5a001dd 100644
--- a/src/main/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreator.java
+++ b/src/main/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreator.java
@@ -27,13 +27,13 @@ package com.github.f4b6a3.ulid.creator;
import java.util.Random;
import java.util.UUID;
-import com.github.f4b6a3.ulid.timestamp.TimestampStrategy;
-import com.github.f4b6a3.ulid.util.UlidUtil;
+import com.github.f4b6a3.ulid.util.UlidConverter;
import com.github.f4b6a3.commons.random.Xorshift128PlusRandom;
import com.github.f4b6a3.commons.util.FingerprintUtil;
import com.github.f4b6a3.commons.util.RandomUtil;
import com.github.f4b6a3.ulid.exception.UlidCreatorException;
-import com.github.f4b6a3.ulid.timestamp.DefaultTimestampStrategy;
+import com.github.f4b6a3.ulid.strategy.TimestampStrategy;
+import com.github.f4b6a3.ulid.strategy.timestamp.DefaultTimestampStrategy;
/**
* Factory that creates lexicographically sortable GUIDs, based on the ULID
@@ -69,7 +69,7 @@ public class UlidBasedGuidCreator {
*
* Return a GUID based on the ULID specification.
*
- * It has two parts:
+ * A ULID has two parts:
*
* 1. A part of 48 bits that represent the amount of milliseconds since Unix
* Epoch, 1 January 1970.
@@ -85,6 +85,9 @@ public class UlidBasedGuidCreator {
*
* The maximum GUIDs that can be generated per millisecond is 2^80.
*
+ * The random part is generated by a secure random number generator:
+ * {@link java.security.SecureRandom}.
+ *
* ### Specification of Universally Unique Lexicographically Sortable ID
*
* #### Components
@@ -116,7 +119,7 @@ public class UlidBasedGuidCreator {
* 2^80 ULIDs within the same millisecond, or cause the random component to
* overflow with less, the generation will fail.
*
- * @return {@link UUID} a UUID value
+ * @return {@link UUID} a GUID value
*
* @throws UlidCreatorException an overrun exception if too many requests are
* made within the same millisecond.
@@ -135,13 +138,14 @@ public class UlidBasedGuidCreator {
}
/**
- * Return a ULID.
+ * Returns a ULID string.
+ *
+ * The returning string is encoded to Crockford's base32.
*
* @return a ULID string
*/
public synchronized String createString() {
- UUID guid = create();
- return UlidUtil.fromUuidToUlid(guid);
+ return UlidConverter.toString(create());
}
/**
@@ -185,7 +189,8 @@ public class UlidBasedGuidCreator {
/**
* Increment the random part of the GUID.
*
- * An exception is thrown when more than 2^80 increment operations are made.
+ * An exception is thrown when more than 2^80 increment operations are made,
+ * although it's extremely unlikely to occur.
*
* @throws UlidCreatorException if an overrun happens.
*/
diff --git a/src/main/java/com/github/f4b6a3/ulid/exception/InvalidUlidException.java b/src/main/java/com/github/f4b6a3/ulid/exception/InvalidUlidException.java
new file mode 100644
index 0000000..7ff70a2
--- /dev/null
+++ b/src/main/java/com/github/f4b6a3/ulid/exception/InvalidUlidException.java
@@ -0,0 +1,34 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Fabio Lima
+ *
+ * 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 com.github.f4b6a3.ulid.exception;
+
+public class InvalidUlidException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public InvalidUlidException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/github/f4b6a3/ulid/exception/UlidCreatorException.java b/src/main/java/com/github/f4b6a3/ulid/exception/UlidCreatorException.java
index 8740403..e6f2078 100644
--- a/src/main/java/com/github/f4b6a3/ulid/exception/UlidCreatorException.java
+++ b/src/main/java/com/github/f4b6a3/ulid/exception/UlidCreatorException.java
@@ -26,8 +26,8 @@ package com.github.f4b6a3.ulid.exception;
public class UlidCreatorException extends RuntimeException {
- private static final long serialVersionUID = 6755381080404981234L;
-
+ private static final long serialVersionUID = 1L;
+
public UlidCreatorException(String message) {
super(message);
}
diff --git a/src/main/java/com/github/f4b6a3/ulid/timestamp/TimestampStrategy.java b/src/main/java/com/github/f4b6a3/ulid/strategy/TimestampStrategy.java
similarity index 96%
rename from src/main/java/com/github/f4b6a3/ulid/timestamp/TimestampStrategy.java
rename to src/main/java/com/github/f4b6a3/ulid/strategy/TimestampStrategy.java
index 57f42f9..e6640b5 100644
--- a/src/main/java/com/github/f4b6a3/ulid/timestamp/TimestampStrategy.java
+++ b/src/main/java/com/github/f4b6a3/ulid/strategy/TimestampStrategy.java
@@ -22,7 +22,7 @@
* SOFTWARE.
*/
-package com.github.f4b6a3.ulid.timestamp;
+package com.github.f4b6a3.ulid.strategy;
public interface TimestampStrategy {
long getTimestamp();
diff --git a/src/main/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategy.java b/src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/DefaultTimestampStrategy.java
similarity index 92%
rename from src/main/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategy.java
rename to src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/DefaultTimestampStrategy.java
index d802ec0..2c9e514 100644
--- a/src/main/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategy.java
+++ b/src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/DefaultTimestampStrategy.java
@@ -22,7 +22,9 @@
* SOFTWARE.
*/
-package com.github.f4b6a3.ulid.timestamp;
+package com.github.f4b6a3.ulid.strategy.timestamp;
+
+import com.github.f4b6a3.ulid.strategy.TimestampStrategy;
public class DefaultTimestampStrategy implements TimestampStrategy {
diff --git a/src/main/java/com/github/f4b6a3/ulid/timestamp/FixedTimestampStretegy.java b/src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/FixedTimestampStretegy.java
similarity index 92%
rename from src/main/java/com/github/f4b6a3/ulid/timestamp/FixedTimestampStretegy.java
rename to src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/FixedTimestampStretegy.java
index 0d568f6..7b7e7a7 100644
--- a/src/main/java/com/github/f4b6a3/ulid/timestamp/FixedTimestampStretegy.java
+++ b/src/main/java/com/github/f4b6a3/ulid/strategy/timestamp/FixedTimestampStretegy.java
@@ -22,7 +22,9 @@
* SOFTWARE.
*/
-package com.github.f4b6a3.ulid.timestamp;
+package com.github.f4b6a3.ulid.strategy.timestamp;
+
+import com.github.f4b6a3.ulid.strategy.TimestampStrategy;
public class FixedTimestampStretegy implements TimestampStrategy {
diff --git a/src/main/java/com/github/f4b6a3/ulid/util/UlidConverter.java b/src/main/java/com/github/f4b6a3/ulid/util/UlidConverter.java
new file mode 100644
index 0000000..fecc005
--- /dev/null
+++ b/src/main/java/com/github/f4b6a3/ulid/util/UlidConverter.java
@@ -0,0 +1,103 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Fabio Lima
+ *
+ * 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 com.github.f4b6a3.ulid.util;
+
+import java.util.UUID;
+
+import com.github.f4b6a3.commons.util.Base32Util;
+import com.github.f4b6a3.commons.util.ByteUtil;
+
+public class UlidConverter {
+
+ private UlidConverter() {
+ }
+
+ /**
+ * Convert a UUID to ULID string
+ *
+ * The returning string is encoded to Crockford's base32.
+ *
+ * The timestamp and random components are encoded separated.
+ *
+ * @param uuid a UUID
+ * @return a ULID
+ */
+ public static String toString(UUID uuid) {
+
+ final long msb = uuid.getMostSignificantBits();
+ final long lsb = uuid.getLeastSignificantBits();
+
+ // Extract timestamp component
+ final long timeNumber = (msb >>> 16);
+ String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
+
+ // Extract randomness component
+ byte[] randBytes = new byte[10];
+ randBytes[0] = (byte) (msb >>> 8);
+ randBytes[1] = (byte) (msb);
+ byte[] lsbBytes = ByteUtil.toBytes(lsb);
+ System.arraycopy(lsbBytes, 0, randBytes, 2, 8);
+ String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
+
+ return timestampComponent + randomnessComponent;
+ }
+
+ /**
+ * Converts a ULID string to a UUID.
+ *
+ * The input string must be encoded to Crockford's base32, following the ULID
+ * specification.
+ *
+ * The timestamp and random components are decoded separated.
+ *
+ * An exception is thrown if the ULID string is invalid.
+ *
+ * @param ulid a ULID
+ * @return a UUID if valid
+ */
+ public static UUID fromString(final String ulid) {
+
+ UlidValidator.validate(ulid);
+
+ // Extract timestamp component
+ final String timestampComponent = ulid.substring(0, 10);
+ final long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
+
+ // Extract randomness component
+ final String randomnessComponent = ulid.substring(10, 26);
+ byte[] randBytes = Base32Util.fromBase32Crockford(randomnessComponent);
+ byte[] lsbBytes = new byte[8];
+ System.arraycopy(randBytes, 2, lsbBytes, 0, 8);
+
+ final long msb = (timeNumber << 16) | ((randBytes[0] << 8) & 0x0000ff00L) | ((randBytes[1]) & 0x000000ffL);
+ final long lsb = ByteUtil.toNumber(lsbBytes);
+
+ return new UUID(msb, lsb);
+ }
+
+ private static String leftPad(String unpadded) {
+ return "0000000000".substring(unpadded.length()) + unpadded;
+ }
+}
diff --git a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
index c40091d..57658eb 100644
--- a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
+++ b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java
@@ -25,237 +25,16 @@
package com.github.f4b6a3.ulid.util;
import java.time.Instant;
-import java.util.UUID;
import com.github.f4b6a3.commons.util.Base32Util;
-import com.github.f4b6a3.commons.util.ByteUtil;
public class UlidUtil {
- // Date: 10889-08-02T05:31:50.655Z
- protected static final long TIMESTAMP_MAX = (long) Math.pow(2, 48) - 1;
-
- protected static final String ULID_PATTERN_STRICT = "^[0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{26}$";
- protected static final String ULID_PATTERN_LOOSE = "^[0-9a-tv-zA-TV-Z]{26}$";
-
private UlidUtil() {
}
- /**
- * Convert a UUID to ULID string
- *
- * @param uuid
- * a UUID
- * @return a ULID
- */
- public static String fromUuidToUlid(UUID uuid) {
-
- final long msb = uuid.getMostSignificantBits();
- final long lsb = uuid.getLeastSignificantBits();
-
- // Extract timestamp component
- final long timeNumber = (msb >>> 16);
- String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
-
- // Extract randomness component
- byte[] randBytes = new byte[10];
- randBytes[0] = (byte) (msb >>> 8);
- randBytes[1] = (byte) (msb);
- byte[] lsbBytes = ByteUtil.toBytes(lsb);
- System.arraycopy(lsbBytes, 0, randBytes, 2, 8);
- String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
-
- return timestampComponent + randomnessComponent;
- }
-
- /**
- * Converts a ULID string to a UUID.
- *
- * An exception is thrown if the ULID string is invalid.
- *
- * @param ulid
- * a ULID
- * @return a UUID if valid
- */
- public static UUID fromUlidToUuid(final String ulid) {
-
- UlidUtil.validate(ulid);
-
- // Extract timestamp component
- final String timestampComponent = ulid.substring(0, 10);
- final long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
-
- // Extract randomness component
- final String randomnessComponent = ulid.substring(10, 26);
- byte[] randBytes = Base32Util.fromBase32Crockford(randomnessComponent);
- byte[] lsbBytes = new byte[8];
- System.arraycopy(randBytes, 2, lsbBytes, 0, 8);
-
- final long msb = (timeNumber << 16) | ((randBytes[0] << 8) & 0x0000ff00L) | ((randBytes[1]) & 0x000000ffL);
- final long lsb = ByteUtil.toNumber(lsbBytes);
-
- return new UUID(msb, lsb);
- }
-
- /**
- * Get the array of bytes from a UUID.
- *
- * @param uuid
- * a UUID
- * @return an array of bytes
- */
- public static byte[] fromUuidToBytes(final UUID uuid) {
- final long msb = uuid.getMostSignificantBits();
- final long lsb = uuid.getLeastSignificantBits();
- final byte[] msbBytes = ByteUtil.toBytes(msb);
- final byte[] lsbBytes = ByteUtil.toBytes(lsb);
- return ByteUtil.concat(msbBytes, lsbBytes);
- }
-
- /**
- * Get a UUID from an array of bytes;
- *
- * @param bytes
- * an array of bytes
- * @return a UUID
- */
- public static UUID fromBytesToUuid(byte[] bytes) {
- byte[] msbBytes = new byte[8];
- System.arraycopy(bytes, 0, msbBytes, 0, 8);
- byte[] lsbBytes = new byte[8];
- System.arraycopy(bytes, 8, lsbBytes, 0, 8);
- final long msb = ByteUtil.toNumber(msbBytes);
- final long lsb = ByteUtil.toNumber(lsbBytes);
- return new UUID(msb, lsb);
- }
-
- /**
- * Convert an array of bytes to a ULID string.
- *
- * @param bytes
- * a byte array
- * @return a ULID string
- */
- public static String fromBytesToUlid(byte[] bytes) {
-
- byte[] timeBytes = new byte[6];
- System.arraycopy(bytes, 0, timeBytes, 0, 6);
- final long timeNumber = ByteUtil.toNumber(timeBytes);
- final String timestampComponent = leftPad(Base32Util.toBase32Crockford(timeNumber));
-
- byte[] randBytes = new byte[10];
- System.arraycopy(bytes, 6, randBytes, 0, 10);
- final String randomnessComponent = Base32Util.toBase32Crockford(randBytes);
-
- return timestampComponent + randomnessComponent;
- }
-
- /**
- * Convert a ULID string to an array of bytes.
- *
- * @param ulid
- * a ULID string
- * @return an array of bytes
- */
- public static byte[] fromUlidToBytes(final String ulid) {
- UlidUtil.validate(ulid);
- byte[] bytes = new byte[16];
-
- final String timestampComponent = ulid.substring(0, 10);
- final long timeNumber = Base32Util.fromBase32CrockfordAsLong(timestampComponent);
- byte[] timeBytes = ByteUtil.toBytes(timeNumber);
- System.arraycopy(timeBytes, 2, bytes, 0, 6);
-
- final String randomnessComponent = ulid.substring(10, 26);
- byte[] randBytes = Base32Util.fromBase32Crockford(randomnessComponent);
- System.arraycopy(randBytes, 0, bytes, 6, 10);
-
- return bytes;
- }
-
- /**
- * Checks if the ULID string is a valid.
- *
- * The validation mode is not strict.
- *
- * See {@link UlidUtil#validate(String, boolean)}.
- *
- * @param ulid
- * a ULID
- */
- protected static void validate(String ulid) {
- validate(ulid, false);
- }
-
- /**
- * Checks if the ULID string is a valid.
- *
- * See {@link UlidUtil#validate(String, boolean)}.
- *
- * @param ulid
- * a ULID
- */
- protected static void validate(String ulid, boolean strict) {
- if (!isValid(ulid, strict)) {
- throw new UlidUtilException(String.format("Invalid ULID: %s.", ulid));
- }
- }
-
- /**
- * Checks if the string is a valid ULID.
- *
- * The validation mode is not strict.
- *
- * See {@link UlidUtil#validate(String, boolean)}.
- */
- public static boolean isValid(String ulid) {
- return isValid(ulid, false);
- }
-
- /**
- * Checks if the string is a valid ULID.
- *
- *
- * Strict validation: checks if the string is in the ULID specification format:
- *
- * - 0123456789ABCDEFGHJKMNPKRS (26 alphanumeric, case insensitive, except iI, lL, oO and uU)
- *
- * Loose validation: checks if the string is in one of these formats:
- *
- * - 0123456789ABCDEFGHIJKLMNOP (26 alphanumeric, case insensitive, except uU)
- *
- *
- * @param ulid
- * a ULID
- * @param strict
- * true for strict validation, false for loose validation
- * @return boolean true if valid
- */
- public static boolean isValid(String ulid, boolean strict) {
-
- if (ulid == null || ulid.isEmpty()) {
- return false;
- }
-
- boolean matches = false;
-
- if (strict) {
- matches = ulid.matches(ULID_PATTERN_STRICT);
- } else {
- String u = ulid.replaceAll("-", "");
- matches = u.matches(ULID_PATTERN_LOOSE);
- }
-
- if (!matches) {
- return false;
- }
-
- long timestamp = extractUnixMilliseconds(ulid);
- return timestamp >= 0 && timestamp <= TIMESTAMP_MAX;
- }
-
public static long extractTimestamp(String ulid) {
- UlidUtil.validate(ulid);
+ UlidValidator.validate(ulid);
return extractUnixMilliseconds(ulid);
}
@@ -265,12 +44,12 @@ public class UlidUtil {
}
public static String extractTimestampComponent(String ulid) {
- UlidUtil.validate(ulid);
+ UlidValidator.validate(ulid);
return ulid.substring(0, 10);
}
public static String extractRandomnessComponent(String ulid) {
- UlidUtil.validate(ulid);
+ UlidValidator.validate(ulid);
return ulid.substring(10, 26);
}
@@ -278,16 +57,4 @@ public class UlidUtil {
String milliseconds = ulid.substring(0, 10);
return Base32Util.fromBase32CrockfordAsLong(milliseconds);
}
-
- private static String leftPad(String unpadded) {
- return "0000000000".substring(unpadded.length()) + unpadded;
- }
-
- public static class UlidUtilException extends RuntimeException {
- private static final long serialVersionUID = 1L;
-
- public UlidUtilException(String message) {
- super(message);
- }
- }
}
diff --git a/src/main/java/com/github/f4b6a3/ulid/util/UlidValidator.java b/src/main/java/com/github/f4b6a3/ulid/util/UlidValidator.java
new file mode 100644
index 0000000..1d4d916
--- /dev/null
+++ b/src/main/java/com/github/f4b6a3/ulid/util/UlidValidator.java
@@ -0,0 +1,87 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Fabio Lima
+ *
+ * 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 com.github.f4b6a3.ulid.util;
+
+import com.github.f4b6a3.ulid.exception.InvalidUlidException;
+
+public class UlidValidator {
+
+ protected static final String ULID_PATTERN = "^[0-9a-tv-zA-TV-Z]{26}$";
+
+ // Date: 10889-08-02T05:31:50.655Z
+ protected static final long TIMESTAMP_MAX = (long) Math.pow(2, 48) - 1;
+
+ private UlidValidator() {
+ }
+
+ /**
+ * Checks if the string is a valid ULID.
+ *
+ * A valid ULID string is a sequence of 26 characters from Crockford's base 32
+ * alphabet.
+ *
+ * Dashes are ignored by this validator.
+ *
+ *
+ * Examples of valid ULID strings:
+ * - 0123456789ABCDEFGHJKMNPKRS (26 alphanumeric, case insensitive, except iI, lL, oO and uU)
+ * - 0123456789ABCDEFGHIJKLMNOP (26 alphanumeric, case insensitive, except uU)
+ * - 0123456789-ABCDEFGHJK-MNPKRS (26 alphanumeric, case insensitive, except iI, lL, oO and uU)
+ * - 0123456789-ABCDEFGHIJ-KLMNOP (26 alphanumeric, case insensitive, except uU, with dashes)
+ *
+ *
+ * @param ulid a ULID
+ * @return boolean true if valid
+ */
+ public static boolean isValid(String ulid) {
+
+ if (ulid == null || ulid.isEmpty()) {
+ return false;
+ }
+
+ String u = ulid.replaceAll("-", "");
+ if (!u.matches(ULID_PATTERN)) {
+ return false;
+ }
+
+ long timestamp = UlidUtil.extractUnixMilliseconds(ulid);
+ return timestamp >= 0 && timestamp <= TIMESTAMP_MAX;
+
+ }
+
+ /**
+ * Checks if the ULID string is a valid.
+ *
+ * See {@link TsidValidator#isValid(String)}.
+ *
+ * @param ulid a ULID string
+ * @throws InvalidUlidException if invalid
+ */
+ protected static void validate(String ulid) {
+ if (!isValid(ulid)) {
+ throw new InvalidUlidException(String.format("Invalid ULID: %s.", ulid));
+ }
+ }
+}
diff --git a/src/test/java/com/github/f4b6a3/TestSuite.java b/src/test/java/com/github/f4b6a3/ulid/TestSuite.java
similarity index 63%
rename from src/test/java/com/github/f4b6a3/TestSuite.java
rename to src/test/java/com/github/f4b6a3/ulid/TestSuite.java
index 1b8b62f..c5cdfee 100644
--- a/src/test/java/com/github/f4b6a3/TestSuite.java
+++ b/src/test/java/com/github/f4b6a3/ulid/TestSuite.java
@@ -1,19 +1,21 @@
-package com.github.f4b6a3;
+package com.github.f4b6a3.ulid;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
-import com.github.f4b6a3.ulid.UlidCreatorTest;
import com.github.f4b6a3.ulid.creator.UlidBasedGuidCreatorTest;
-import com.github.f4b6a3.ulid.timestamp.DefaultTimestampStrategyTest;
+import com.github.f4b6a3.ulid.ulid.UlidCreatorTest;
+import com.github.f4b6a3.ulid.util.UlidConverterTest;
import com.github.f4b6a3.ulid.util.UlidUtilTest;
+import com.github.f4b6a3.ulid.util.UlidValidatorTest;
@RunWith(Suite.class)
@Suite.SuiteClasses({
- DefaultTimestampStrategyTest.class,
- UlidBasedGuidCreatorTest.class,
- UlidUtilTest.class,
UlidCreatorTest.class,
+ UlidBasedGuidCreatorTest.class,
+ UlidConverterTest.class,
+ UlidUtilTest.class,
+ UlidValidatorTest.class,
})
/**
diff --git a/src/test/java/com/github/f4b6a3/UniquenessTest.java b/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java
similarity index 97%
rename from src/test/java/com/github/f4b6a3/UniquenessTest.java
rename to src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java
index ea00a26..dbb33c4 100644
--- a/src/test/java/com/github/f4b6a3/UniquenessTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/UniquenessTest.java
@@ -1,4 +1,4 @@
-package com.github.f4b6a3;
+package com.github.f4b6a3.ulid;
import java.util.HashSet;
import java.util.UUID;
@@ -6,7 +6,7 @@ import java.util.UUID;
import com.github.f4b6a3.ulid.UlidCreator;
import com.github.f4b6a3.ulid.creator.UlidBasedGuidCreator;
import com.github.f4b6a3.ulid.exception.UlidCreatorException;
-import com.github.f4b6a3.ulid.timestamp.FixedTimestampStretegy;
+import com.github.f4b6a3.ulid.strategy.timestamp.FixedTimestampStretegy;
/**
*
diff --git a/src/test/java/com/github/f4b6a3/Benchmarks.java b/src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java
similarity index 98%
rename from src/test/java/com/github/f4b6a3/Benchmarks.java
rename to src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java
index 2eddf2e..7bbd0f1 100644
--- a/src/test/java/com/github/f4b6a3/Benchmarks.java
+++ b/src/test/java/com/github/f4b6a3/ulid/bench/Benchmarks.java
@@ -1,4 +1,4 @@
-package com.github.f4b6a3;
+package com.github.f4b6a3.ulid.bench;
// Add theese dependencies to pom.xml:
//
diff --git a/src/test/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreatorTest.java b/src/test/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreatorTest.java
index 9068805..af6794f 100644
--- a/src/test/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreatorTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/creator/UlidBasedGuidCreatorTest.java
@@ -7,7 +7,7 @@ import org.junit.Test;
import com.github.f4b6a3.commons.random.Xorshift128PlusRandom;
import com.github.f4b6a3.ulid.exception.UlidCreatorException;
-import com.github.f4b6a3.ulid.timestamp.FixedTimestampStretegy;
+import com.github.f4b6a3.ulid.strategy.timestamp.FixedTimestampStretegy;
import static org.junit.Assert.*;
diff --git a/src/test/java/com/github/f4b6a3/demo/DemoTest.java b/src/test/java/com/github/f4b6a3/ulid/demo/DemoTest.java
similarity index 83%
rename from src/test/java/com/github/f4b6a3/demo/DemoTest.java
rename to src/test/java/com/github/f4b6a3/ulid/demo/DemoTest.java
index c46baca..ed951e2 100644
--- a/src/test/java/com/github/f4b6a3/demo/DemoTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/demo/DemoTest.java
@@ -1,4 +1,4 @@
-package com.github.f4b6a3.demo;
+package com.github.f4b6a3.ulid.demo;
import com.github.f4b6a3.ulid.UlidCreator;
@@ -10,7 +10,7 @@ public class DemoTest {
int max = 100;
System.out.println(HORIZONTAL_LINE);
- System.out.println("### ULID");
+ System.out.println("### ULID string");
System.out.println(HORIZONTAL_LINE);
for (int i = 0; i < max; i++) {
@@ -18,7 +18,7 @@ public class DemoTest {
}
System.out.println(HORIZONTAL_LINE);
- System.out.println("### GUID");
+ System.out.println("### ULID-based GUID");
System.out.println(HORIZONTAL_LINE);
for (int i = 0; i < max; i++) {
diff --git a/src/test/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategyTest.java b/src/test/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategyTest.java
deleted file mode 100644
index 5b8d251..0000000
--- a/src/test/java/com/github/f4b6a3/ulid/timestamp/DefaultTimestampStrategyTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.github.f4b6a3.ulid.timestamp;
-
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-public class DefaultTimestampStrategyTest {
- @Test
- public void testVoid() {
- assertTrue("void test", true);
- }
-}
diff --git a/src/test/java/com/github/f4b6a3/ulid/UlidCreatorTest.java b/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java
similarity index 95%
rename from src/test/java/com/github/f4b6a3/ulid/UlidCreatorTest.java
rename to src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java
index c16389d..0d732f5 100644
--- a/src/test/java/com/github/f4b6a3/ulid/UlidCreatorTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/ulid/UlidCreatorTest.java
@@ -1,4 +1,4 @@
-package com.github.f4b6a3.ulid;
+package com.github.f4b6a3.ulid.ulid;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -6,6 +6,7 @@ import org.junit.Test;
import com.github.f4b6a3.ulid.UlidCreator;
import com.github.f4b6a3.ulid.creator.UlidBasedGuidCreator;
import com.github.f4b6a3.ulid.util.UlidUtil;
+import com.github.f4b6a3.ulid.util.UlidValidator;
import static org.junit.Assert.*;
import java.util.Arrays;
@@ -54,7 +55,7 @@ public class UlidCreatorTest {
assertTrue("ULID is null", ulid != null);
assertTrue("ULID is empty", !ulid.isEmpty());
assertTrue("ULID length is wrong ", ulid.length() == ULID_LENGTH);
- assertTrue("ULID is not valid", UlidUtil.isValid(ulid, /* strict */ true));
+ assertTrue("ULID is not valid", UlidValidator.isValid(ulid));
}
}
diff --git a/src/test/java/com/github/f4b6a3/ulid/util/UlidConverterTest.java b/src/test/java/com/github/f4b6a3/ulid/util/UlidConverterTest.java
new file mode 100644
index 0000000..7199d76
--- /dev/null
+++ b/src/test/java/com/github/f4b6a3/ulid/util/UlidConverterTest.java
@@ -0,0 +1,35 @@
+package com.github.f4b6a3.ulid.util;
+
+import static org.junit.Assert.*;
+import java.util.UUID;
+
+import org.junit.Test;
+
+import com.github.f4b6a3.ulid.UlidCreator;
+import com.github.f4b6a3.ulid.util.UlidConverter;
+import com.github.f4b6a3.ulid.util.UlidValidator;
+
+public class UlidConverterTest {
+
+ private static final int ULID_LENGTH = 26;
+ private static final int DEFAULT_LOOP_MAX = 100_000;
+
+ @Test
+ public void testToAndFromUlid() {
+
+ for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
+
+ UUID uuid1 = UlidCreator.getUlid();
+ String ulid = UlidConverter.toString(uuid1);
+
+ assertTrue("ULID is null", ulid != null);
+ assertTrue("ULID is empty", !ulid.isEmpty());
+ assertTrue("ULID length is wrong ", ulid.length() == ULID_LENGTH);
+ assertTrue("ULID is not valid", UlidValidator.isValid(ulid));
+
+ UUID uuid2 = UlidConverter.fromString(ulid);
+ assertEquals("Result ULID is different from original ULID", uuid1, uuid2);
+
+ }
+ }
+}
diff --git a/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java b/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
index e41b7a1..173af03 100644
--- a/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
+++ b/src/test/java/com/github/f4b6a3/ulid/util/UlidUtilTest.java
@@ -2,14 +2,13 @@ package com.github.f4b6a3.ulid.util;
import static org.junit.Assert.*;
import java.time.Instant;
-import java.util.UUID;
import org.junit.Test;
-import com.github.f4b6a3.ulid.util.UlidUtil.UlidUtilException;
import com.github.f4b6a3.commons.util.Base32Util;
import com.github.f4b6a3.commons.util.ByteUtil;
-import com.github.f4b6a3.ulid.UlidCreator;
+import com.github.f4b6a3.ulid.exception.InvalidUlidException;
+import com.github.f4b6a3.ulid.util.UlidUtil;
public class UlidUtilTest {
@@ -19,13 +18,10 @@ public class UlidUtilTest {
private static final long TIMESTAMP_MAX = 281474976710655l; // 2^48 - 1
- private static final int ULID_LENGTH = 26;
- private static final int DEFAULT_LOOP_MAX = 100_000;
-
private static final String[] EXAMPLE_DATES = { "1970-01-01T00:00:00.000Z", "1985-10-26T01:16:00.123Z",
"2001-09-09T01:46:40.456Z", "2020-01-15T14:30:33.789Z", "2038-01-19T03:14:07.321Z" };
- @Test(expected = UlidUtilException.class)
+ @Test(expected = InvalidUlidException.class)
public void testExtractTimestamp() {
String ulid = "0000000000" + EXAMPLE_RANDOMNESS;
@@ -95,155 +91,6 @@ public class UlidUtilTest {
assertEquals(expected, result);
}
- @Test
- public void testIsValidLoose() {
-
- String ulid = null; // Null
- assertFalse("Null ULID should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = ""; // length: 0
- assertFalse("ULID with empty string should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = EXAMPLE_ULID; // All upper case
- assertTrue("Ulid in upper case should valid.", UlidUtil.isValid(ulid));
-
- ulid = "0123456789abcdefghjklmnpqr"; // All lower case
- assertTrue("ULID in lower case should be valid.", UlidUtil.isValid(ulid));
-
- ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
- assertTrue("Ulid in upper and lower case should valid.", UlidUtil.isValid(ulid));
-
- ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
- assertFalse("ULID length lower than 26 should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
- assertFalse("ULID length greater than 26 should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = "u123456789ABCDEFGHJKMNPQRS"; // Letter u
- assertFalse("ULID with 'u' or 'U' should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = "#123456789ABCDEFGHJKMNPQRS"; // Special char
- assertFalse("ULID with special chars should be invalid.", UlidUtil.isValid(ulid));
-
- ulid = "01234-56789-ABCDEFGHJKMNPQRS"; // Hiphens
- assertTrue("ULID with hiphens should be valid.", UlidUtil.isValid(ulid));
-
- ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // timestamp > (2^48)-1
- assertFalse("ULID with timestamp greater than (2^48)-1 should be invalid.", UlidUtil.isValid(ulid));
- }
-
- @Test
- public void testIsValidStrict() {
- boolean strict = true;
-
- String ulid = null; // Null
- assertFalse("Null ULID should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = ""; // length: 0
- assertFalse("ULID with empty string should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = EXAMPLE_ULID; // All upper case
- assertTrue("ULID in upper case should valid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "0123456789abcdefghjkmnpqrs"; // All lower case
- assertTrue("ULID in lower case should be valid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
- assertTrue("ULID in upper and lower case should valid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
- assertFalse("ULID length lower than 26 should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
- assertFalse("ULID length greater than 26 should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "i123456789ABCDEFGHJKMNPQRS"; // Letter i
- assertFalse("ULID with 'i' or 'I' should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "L123456789ABCDEFGHJKMNPQRS"; // letter L
- assertFalse("ULID with 'l' or 'L' should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "o123456789ABCDEFGHJKMNPQRS"; // letter o
- assertFalse("ULID with 'o' or 'O' should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "u123456789ABCDEFGHJKMNPQRS"; // letter u
- assertFalse("ULID with 'u' or 'U' should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "#123456789ABCDEFGHJKMNPQRS"; // Special char
- assertFalse("ULID with special chars should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "01234-56789-ABCDEFGHJKMNPQRS"; // Hyphens
- assertFalse("ULID with hiphens should be invalid in strict mode.", UlidUtil.isValid(ulid, strict));
-
- ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // timestamp > (2^48)-1
- assertFalse("ULID with timestamp greater than (2^48)-1 should be invalid.", UlidUtil.isValid(ulid));
- }
-
- @Test
- public void testToAndFromUlid() {
-
- for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
-
- UUID uuid1 = UlidCreator.getUlid();
- String ulid = UlidUtil.fromUuidToUlid(uuid1);
-
- assertTrue("ULID is null", ulid != null);
- assertTrue("ULID is empty", !ulid.isEmpty());
- assertTrue("ULID length is wrong ", ulid.length() == ULID_LENGTH);
- assertTrue("ULID is not valid", UlidUtil.isValid(ulid, /* strict */
- true));
-
- UUID uuid2 = UlidUtil.fromUlidToUuid(ulid);
- assertEquals("Result ULID is different from original ULID", uuid1, uuid2);
-
- }
- }
-
- @Test
- public void testToAndFromBytes() {
- for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
- String ulid1 = UlidCreator.getUlidString();
- byte[] bytes = UlidUtil.fromUlidToBytes(ulid1);
- String ulid2 = UlidUtil.fromBytesToUlid(bytes);
-
- // Check ULID 1
- assertTrue(ulid1 != null);
- assertTrue(!ulid1.isEmpty());
- assertTrue(ulid1.length() == ULID_LENGTH);
- assertTrue(UlidUtil.isValid(ulid1, /* strict */ true));
-
- // Check ULID 2
- assertTrue(ulid2 != null);
- assertTrue(!ulid2.isEmpty());
- assertTrue(ulid2.length() == ULID_LENGTH);
- assertTrue(UlidUtil.isValid(ulid2, /* strict */ true));
-
- assertEquals(ulid1, ulid2);
- }
- }
-
- @Test
- public void testFromUuidToBytes() {
- for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
- UUID uuid1 = UlidCreator.getUlid();
- byte[] bytes = UlidUtil.fromUuidToBytes(uuid1);
- long msb = ByteUtil.toNumber(ByteUtil.copy(bytes, 0, 8));
- long lsb = ByteUtil.toNumber(ByteUtil.copy(bytes, 8, 16));
- UUID uuid2 = new UUID(msb, lsb);
- assertEquals(uuid1, uuid2);
- }
- }
-
- @Test
- public void testFromBytesToUuid() {
- for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
- UUID uuid1 = UlidCreator.getUlid();
- byte[] bytes = UlidUtil.fromUuidToBytes(uuid1);
- UUID uuid2 = UlidUtil.fromBytesToUuid(bytes);
- assertEquals(uuid1, uuid2);
- }
- }
-
private String leftPad(String unpadded) {
return "0000000000".substring(unpadded.length()) + unpadded;
}
diff --git a/src/test/java/com/github/f4b6a3/ulid/util/UlidValidatorTest.java b/src/test/java/com/github/f4b6a3/ulid/util/UlidValidatorTest.java
new file mode 100644
index 0000000..936ab01
--- /dev/null
+++ b/src/test/java/com/github/f4b6a3/ulid/util/UlidValidatorTest.java
@@ -0,0 +1,47 @@
+package com.github.f4b6a3.ulid.util;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.github.f4b6a3.ulid.util.UlidValidator;
+
+public class UlidValidatorTest {
+
+ @Test
+ public void testIsValidStrict() {
+
+ String ulid = null; // Null
+ assertFalse("Null ULID should be invalid.", UlidValidator.isValid(ulid));
+
+ ulid = ""; // length: 0
+ assertFalse("ULID with empty string should be invalid .", UlidValidator.isValid(ulid));
+
+ ulid = "0123456789ABCDEFGHJKMNPQRS"; // All upper case
+ assertTrue("ULID in upper case should valid.", UlidValidator.isValid(ulid));
+
+ ulid = "0123456789abcdefghjklmnpqr"; // All lower case
+ assertTrue("ULID in lower case should be valid.", UlidValidator.isValid(ulid));
+
+ ulid = "0123456789AbCdEfGhJkMnPqRs"; // Mixed case
+ assertTrue("Ulid in upper and lower case should valid.", UlidValidator.isValid(ulid));
+
+ ulid = "0123456789ABCDEFGHJKLMNPQ"; // length: 25
+ assertFalse("ULID length lower than 26 should be invalid.", UlidValidator.isValid(ulid));
+
+ ulid = "0123456789ABCDEFGHJKMNPQZZZ"; // length: 27
+ assertFalse("ULID length greater than 26 should be invalid.", UlidValidator.isValid(ulid));
+
+ ulid = "u123456789ABCDEFGHJKMNPQRS"; // Letter u
+ assertFalse("ULID with 'u' or 'U' should be invalid.", UlidValidator.isValid(ulid));
+
+ ulid = "#123456789ABCDEFGHJKMNPQRS"; // Special char
+ assertFalse("ULID with special chars should be invalid.", UlidValidator.isValid(ulid));
+
+ ulid = "01234-56789-ABCDEFGHJKMNPQRS"; // Hyphens
+ assertTrue("ULID with hiphens should be valid.", UlidValidator.isValid(ulid));
+
+ ulid = "8ZZZZZZZZZABCDEFGHJKMNPQRS"; // timestamp > (2^48)-1
+ assertFalse("ULID with timestamp greater than (2^48)-1 should be invalid.", UlidValidator.isValid(ulid));
+ }
+}