View Javadoc
1   package com.buralotech.oss.identifier.uuid;
2   
3   import com.fasterxml.uuid.Generators;
4   import com.fasterxml.uuid.NoArgGenerator;
5   import com.fasterxml.uuid.UUIDType;
6   import com.fasterxml.uuid.impl.UUIDUtil;
7   
8   import java.nio.ByteBuffer;
9   import java.nio.ByteOrder;
10  import java.time.Instant;
11  import java.util.regex.Pattern;
12  
13  /**
14   * Encapsulates the logic that is specific to the standard type 7 UUID format.
15   */
16  public class UUIDVersion7Delegate implements UUIDVersionDelegate {
17  
18      /**
19       * The regular expression pattern used to validate identifiers.
20       */
21      private static final Pattern PATTERN = Pattern.compile("[0-9a-zA-Z_-]{8}[RSTU][0-9a-zA-Z_-][159DHLPTXaeimquy][0-9a-zA-Z_-]{10}[FVk-]");
22  
23      /**
24       * Used to generate time based UUIDs.
25       */
26      private final NoArgGenerator generator = Generators.timeBasedEpochRandomGenerator();
27  
28      /**
29       * Generate an identifier using an underlying UUID generator.
30       *
31       * @return The generated identifier as a byte array.
32       */
33      @Override
34      public byte[] generate() {
35          final var uuid = generator.generate();
36          return UUIDUtil.asByteArray(uuid);
37      }
38  
39      /**
40       * Check that the binary representation is valid. The service has already checked that it is non-null and a valid length.
41       *
42       * @param binary The binary representation.
43       * @return {@code true} if the binary representation is valid. Otherwise, {@code false}.
44       */
45      @Override
46      public boolean isValidBinary(final byte[] binary) {
47          return ((binary[6] & 0xf0) == 0x70) && ((binary[8] & 0xc0) == 0x80);
48      }
49  
50      /**
51       * Check that the text representation is valid. The service has already checked that it is non-null and a valid length.
52       *
53       * @param text The binary representation.
54       * @return {@code true} if the text representation is valid. Otherwise, {@code false}.
55       */
56      @Override
57      public boolean isValidText(final String text) {
58          return PATTERN.matcher(text).matches();
59      }
60  
61      /**
62       * Extract the timestamp from the UUID.
63       *
64       * @param binary The binary representation of the UUID.
65       * @return The timestamp as an Instant.
66       */
67      @Override
68      public Instant toInstant(final byte[] binary) {
69          return Instant.ofEpochMilli(UUIDUtil.extractTimestamp(UUIDUtil.constructUUID(UUIDType.TIME_BASED_EPOCH, binary)));
70      }
71  
72      /**
73       * Create a UUID as a byte array from a timestamp.
74       *
75       * @param ticks  The timestamp in 100 nanoseconds.
76       * @param suffix The second portion of the UUID.
77       * @return The UUID as a byte array.
78       */
79      @Override
80      public byte[] fromTicks(final long ticks, final long suffix) {
81          final var bytes = new byte[16];
82          final var buffer = ByteBuffer.wrap(bytes);
83          buffer.order(ByteOrder.BIG_ENDIAN);
84          buffer.putLong((((ticks - 122192928000000000L) / 10000L) << 16) | 0x07000L | (suffix & 0x0FFFL));
85          buffer.putLong(suffix);
86          return bytes;
87      }
88  }