001/* 002 * ============================================================================ 003 * Copyright © 2002-2026 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * http://www.gnu.org/licenses/lgpl.html 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 011 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 012 * License for the specific language governing permissions and limitations 013 * under the License. 014 */ 015 016package org.tquadrat.foundation.util; 017 018import static java.lang.Long.toBinaryString; 019import static java.lang.Math.abs; 020import static java.lang.System.currentTimeMillis; 021import static java.util.Locale.ROOT; 022import static java.util.UUID.fromString; 023import static org.apiguardian.api.API.Status.INTERNAL; 024import static org.apiguardian.api.API.Status.STABLE; 025import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_String_ARRAY; 026import static org.tquadrat.foundation.lang.CommonConstants.UTF8; 027import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 028import static org.tquadrat.foundation.lang.Objects.requireNotBlankArgument; 029import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 030import static org.tquadrat.foundation.lang.Objects.requireValidIntegerArgument; 031import static org.tquadrat.foundation.util.SecurityUtils.calculateMD5Hash; 032import static org.tquadrat.foundation.util.SecurityUtils.calculateSHA1Hash; 033import static org.tquadrat.foundation.util.StringUtils.isNotEmpty; 034import static org.tquadrat.foundation.util.StringUtils.repeat; 035import static org.tquadrat.foundation.util.StringUtils.splitString; 036import static org.tquadrat.foundation.util.SystemUtils.createPseudoNodeId; 037import static org.tquadrat.foundation.util.SystemUtils.currentTimeNanos; 038import static org.tquadrat.foundation.util.SystemUtils.getNodeId; 039import static org.tquadrat.foundation.util.SystemUtils.getRandom; 040import static org.tquadrat.foundation.util.SystemUtils.repose; 041 042import java.math.BigInteger; 043import java.util.Map; 044import java.util.TreeMap; 045import java.util.UUID; 046import java.util.concurrent.atomic.AtomicInteger; 047import java.util.stream.IntStream; 048 049import org.apiguardian.api.API; 050import org.tquadrat.foundation.annotation.ClassVersion; 051import org.tquadrat.foundation.annotation.UtilityClass; 052import org.tquadrat.foundation.exception.EmptyArgumentException; 053import org.tquadrat.foundation.exception.NullArgumentException; 054import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 055import org.tquadrat.foundation.exception.UnsupportedEnumError; 056import org.tquadrat.foundation.lang.AutoLock; 057 058/** 059 * <p>{@summary This static class provides some utility methods that are helpful when 060 * working with unique ids.}</p> 061 * <p>All methods in this class are final, no instance of this class is 062 * allowed.</p> 063 * <p>First it extends the capabilities of the class 064 * {@link UUID} 065 * that is a part of the Java Runtime library; it implements Universal Unique 066 * ids as defined through RFC 4122. It extends the 067 * capabilities of the Java Runtime class 068 * {@link UUID}.</p> 069 * 070 * <h2>RFC 4122 UUID</h2> 071 * <p>The methods 072 * {@link #nameUUIDFromBytes(byte[],HashType)}, 073 * {@link #nameUUIDFromString(CharSequence,HashType)}, 074 * {@link #nameUUIDFromString(UUID, CharSequence,HashType)}, 075 * {@link #randomUUID()}, 076 * {@link #sequenceUUID(long,long)}, 077 * {@link #timebasedUUID()}, 078 * {@link #timebasedUUID(long)}, 079 * {@link #timebasedUUIDFromNodeName(CharSequence)}, 080 * and 081 * {@link #uuidFromString(CharSequence)} 082 * do all create a 083 * {@link UUID} 084 * instance, but {@code randomUUID()} will delegate to the method with the 085 * same name of the class {@code UUID} itself, while 086 * {@code uuidFromString(CharSequence)} delegates to 087 * {@link UUID#fromString(String)}. 088 * {@code nameUUIDFromBytes(byte[],HashType)} delegates to 089 * {@link UUID#nameUUIDFromBytes(byte[])} 090 * for {@code hashType} equal to 091 * {@link HashType#HASH_MD5}.</p> 092 * <p>Currently, this class supports only the generation of UUIDs with the 093 * types 1 (not supported by 094 * {@link java.util.UUID}), 095 * 3, 4, and 5, although the method 096 * {@link #uuidFromString(CharSequence)} 097 * is also capable of converting UUID Strings representing the type 2 098 * into valid UUID instances.</p> 099 * <p>The type 0 as generated by 100 * {@link #sequenceUUID(long,long)} 101 * is not defined by RFC 4122.</p> 102 * 103 * <h3>The sample Implementation for a UUID Generator</h3> 104 * <p>The source code for this sample implementation in C was taken from 105 * <a href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a>.</p> 106 * <ul> 107 * <li><a href="doc-files/uuid.h"><code>uuid.h</code></a></li> 108 * <li><a href="doc-files/uuid.c"><code>uuid.c</code></a></li> 109 * <li><a href="doc-files/sysdep.h"><code>sysdep.h</code></a></li> 110 * <li><a href="doc-files/sysdep.c"><code>sysdep.c</code></a></li> 111 * <li><a href="doc-files/utest.c"><code>utest.c</code></a></li> 112 * <li><a href="doc-files/copyrt.h"><code>copyrt.h</code></a></li> 113 * </ul> 114 * <p>The appendix C of RFC 4122 also lists the name space IDs for some 115 * potentially interesting name spaces, as initialized C structures and in the 116 * string representation defined by the RFC.</p> 117 * <div class="source-container"><pre>/* Name string is a fully-qualified domain name */ 118 * uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ 119 * 0x6ba7b810, 120 * 0x9dad, 121 * 0x11d1, 122 * 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 123 * }; 124 * 125 * /* Name string is a URL */ 126 * uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */ 127 * 0x6ba7b811, 128 * 0x9dad, 129 * 0x11d1, 130 * 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 131 * }; 132 * 133 * /* Name string is an ISO OID */ 134 * uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */ 135 * 0x6ba7b812, 136 * 0x9dad, 137 * 0x11d1, 138 * 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 139 * }; 140 * 141 * /* Name string is an X.500 DN (in DER or a text output format) */ 142 * uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */ 143 * 0x6ba7b814, 144 * 0x9dad, 145 * 0x11d1, 146 * 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 147 * };</pre></div> 148 * 149 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 150 * @version $Id: UniqueIdUtils.java 1258 2026-06-04 18:33:06Z tquadrat $ 151 * @since 0.0.5 152 * 153 * @see UUID#nameUUIDFromBytes(byte[]) 154 * @see UUID#randomUUID() 155 * @see UUID#fromString(String) 156 * @see <a href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a> 157 * 158 * @UMLGraph.link 159 */ 160@ClassVersion( sourceVersion = "$Id: UniqueIdUtils.java 1258 2026-06-04 18:33:06Z tquadrat $" ) 161@API( status = STABLE, since = "0.0.5" ) 162@UtilityClass 163public final class UniqueIdUtils 164{ 165 /*------------------*\ 166 ====** Enum Declaration **================================================= 167 \*------------------*/ 168 /** 169 * Two different hash types are used for name-based UUIDs. 170 * 171 * @UMLGraph.link 172 */ 173 public static enum HashType 174 { 175 /** 176 * UUIDs of type 3 are using MD5 hashes. 177 */ 178 HASH_MD5, 179 180 /** 181 * UUIDs of type 5 are using SHA-1 hashes. 182 */ 183 HASH_SHA 184 } 185 // enum HashType 186 187 /*-----------*\ 188 ====** Constants **======================================================== 189 \*-----------*/ 190 /** 191 * The bit mask used for the conversion from and to a number. 192 */ 193 private static final BigInteger BIT_MASK = BigInteger.valueOf( 0xFFFFFFFFFFFFFFFFL ); 194 195 /** 196 * Divisor for the calculation of the timestamp. 197 */ 198 private static final BigInteger ONE_HUNDRED = BigInteger.valueOf( 100 ); 199 200 /** 201 * <p>{@summary The name for the internal system property for the flag 202 * controlling that only pseudo node ids should be used to generate 203 * {@link UUID UUID} 204 * instances of type 1: {@value}.}</p> 205 * <p>A value of {@true} means that only pseudo ids will be used 206 * throughout the current run of the program, while {@false} 207 * (the default) means that a MAC address is used for the calculation of a 208 * node id, if available.</p> 209 * <p>This system property is not necessarily configured.</p> 210 * 211 * @see SystemUtils#PROPERTY_NODE_ID 212 */ 213 @API( status = STABLE, since = "0.0.5" ) 214 public static final String PROPERTY_USE_PSEUDO_NODE_ID = "org.tquadrat.foundation.util.UniqueIdUtils.UsePseudoNodeId"; 215 216 /** 217 * The character count for a {@link UUID}: {@value}. 218 */ 219 @API( status = STABLE, since = "0.0.5" ) 220 public static final int UUID_Size = 36; 221 222 /*------------*\ 223 ====** Attributes **======================================================= 224 \*------------*/ 225 /** 226 * The clock sequence. 227 */ 228 private static volatile long m_ClockSeq = Long.MIN_VALUE; 229 230 /** 231 * The last time when a clock sequence was requested. 232 */ 233 private static volatile long m_LastClockSeqRequest = 0L; 234 235 /** 236 * The counter for version 7 UUIDs. 237 */ 238 private static final AtomicInteger m_UUID7Counter = new AtomicInteger( getRandom().nextInt() ); 239 240 /*------------------------*\ 241 ====** Static Initialisations **=========================================== 242 \*------------------------*/ 243 /** 244 * <p>{@summary The digits that are used for an XML safe UUID.} The first 245 * array holds the original digits, the second array those for the XML 246 * id.</p> 247 */ 248 @API( status = INTERNAL, since = "0.3.0" ) 249 private static final char [][] m_UUIDXMLDigits; 250 251 static 252 { 253 final var fromXML = "-0123456789ABCDEFGHIJKL".toCharArray(); 254 @SuppressWarnings( "SpellCheckingInspection" ) 255 final var toXML = "XABCDEFGHJKLMNPRSTUVWYZ".toCharArray(); 256 m_UUIDXMLDigits = new char[2][fromXML.length]; 257 m_UUIDXMLDigits [0] = fromXML; 258 m_UUIDXMLDigits [1] = toXML; 259 } 260 261 /*--------------*\ 262 ====** Constructors **===================================================== 263 \*--------------*/ 264 /** 265 * No instance of this class is allowed! 266 */ 267 private UniqueIdUtils() { throw new PrivateConstructorForStaticClassCalledError( UniqueIdUtils.class ); } 268 269 /*------------------------*\ 270 ====** Static Initialisations **=========================================== 271 \*------------------------*/ 272 /** 273 * The guard for the clock sequence. 274 */ 275 private static final AutoLock m_ClockSeqGuard; 276 277 /** 278 * The dummy node id that is used to generate UUIDs, if required. This is 279 * always a random value. 280 */ 281 @SuppressWarnings( "FieldNamingConvention" ) 282 private static final long m_DummyNodeId; 283 284 /** 285 * The UUIDs for the predefined name spaces, according to RFC 4122. 286 */ 287 @SuppressWarnings( "StaticCollection" ) 288 private static final Map<String,UUID> m_Namespaces; 289 290 /** 291 * The node id that is used to generate UUIDs. This is either the MAC 292 * address of one of the NICs in the current system, or a random value. 293 */ 294 @SuppressWarnings( "FieldNamingConvention" ) 295 private static final long m_NodeId; 296 297 /** 298 * This flag controls if 299 * {@link #m_NodeId} 300 * is forced to be a random value.<br> 301 * <br>It will be controlled by the system property 302 * "{@value #PROPERTY_USE_PSEUDO_NODE_ID}".<br> 303 * <br>Using a pseudo node id would generate anonymous UUIDs. 304 */ 305 @SuppressWarnings( "FieldNamingConvention" ) 306 private static final boolean m_UsePseudoNodeId; 307 308 /** 309 * The max UUID. 310 */ 311 @API( status = STABLE, since = "0.0.5" ) 312 public static final UUID UUID_MAX; 313 314 /** 315 * The nil UUID. 316 */ 317 @API( status = STABLE, since = "0.0.5" ) 318 public static final UUID UUID_NIL; 319 320 static 321 { 322 //---* Create the locks *---------------------------------------------- 323 m_ClockSeqGuard = AutoLock.of(); 324 325 //---* Create the dummy node id *-------------------------------------- 326 m_DummyNodeId = createPseudoNodeId(); 327 328 //---* Get the flag that controls the generation of the node id *------ 329 //noinspection AccessOfSystemProperties 330 m_UsePseudoNodeId = Boolean.getBoolean( PROPERTY_USE_PSEUDO_NODE_ID ); 331 332 //---* Retrieve the node id *------------------------------------------ 333 m_NodeId = m_UsePseudoNodeId ? createPseudoNodeId() : getNodeId(); 334 335 //---* Create the namespaces *----------------------------------------- 336 final Map<String,UUID> namespaces = new TreeMap<>(); 337 338 namespaces.put( "DNS", fromString( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) ); 339 namespaces.put( "URL", fromString( "6ba7b811-9dad-11d1-80b4-00c04fd430c8" ) ); 340 namespaces.put( "ISO_OID", fromString( "6ba7b812-9dad-11d1-80b4-00c04fd430c8" ) ); 341 namespaces.put( "X500", fromString( "6ba7b814-9dad-11d1-80b4-00c04fd430c8" ) ); 342 343 var internalNamespace = "tquadrat"; 344 namespaces.put( internalNamespace, UUID.nameUUIDFromBytes( internalNamespace.getBytes( UTF8 ) ) ); 345 internalNamespace = "Foundation"; 346 namespaces.put( internalNamespace, UUID.nameUUIDFromBytes( internalNamespace.getBytes( UTF8 ) ) ); 347 348 m_Namespaces = Map.copyOf( namespaces ); 349 350 //---* The UUIDs *----------------------------------------------------- 351 UUID_NIL = new UUID( 0, 0 ); 352 UUID_MAX = new UUID( 0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL ); 353 } 354 355 /*---------*\ 356 ====** Methods **========================================================== 357 \*---------*/ 358 /** 359 * Converts an XML safe id that was created through 360 * {@link #toXMLId(UUID)} 361 * back to a UUID. 362 * 363 * @param input The XML safe id. 364 * @return The UUID. 365 * @throws IllegalArgumentException The given XML safe id cannot be 366 * converted to a UUID. 367 */ 368 @API( status = STABLE, since = "0.3.0" ) 369 public static final UUID fromXMLId( final CharSequence input ) 370 { 371 final var radix = m_UUIDXMLDigits [0].length - 1; 372 final var numbers = new long [2]; 373 374 final var parts = splitString( requireNotBlankArgument( input, "input" ).toString().toUpperCase( ROOT ), "-" ); 375 final var message = "Cannot convert '%s' to a UUID!".formatted( input ); 376 requireValidIntegerArgument( parts.length, "input", length -> length == 2, (_,_) -> message ); 377 for( var i = 0; i < parts.length; ++i ) 378 { 379 final var buffer = new StringBuilder(); 380 for( final var c : parts [i].toUpperCase( ROOT ).toCharArray() ) 381 { 382 IntStream.range( 0, m_UUIDXMLDigits[1].length ) 383 .filter( index -> m_UUIDXMLDigits[1][index] == c ) 384 .findFirst() 385 .ifPresentOrElse( index -> buffer.append( m_UUIDXMLDigits[0][index] ), () -> {throw new IllegalArgumentException( message );} ); 386 } 387 numbers [i] = Long.parseLong( buffer.toString().toLowerCase( ROOT ), radix ); 388 } 389 390 final var retValue = new UUID( numbers [0], numbers [1] ); 391 392 //---* Done *---------------------------------------------------------- 393 return retValue; 394 } // fromXMLId() 395 396 /** 397 * <p>{@summary Returns the clock sequence.} It will be initialised with a 398 * random number on each time the program starts, and it remains unchanged 399 * until the system detects a clock shift; in that case, it will be 400 * increased by one.</p> 401 * <p>An overflow for the clock sequence is possible, but does not harm.</p> 402 * 403 * @param currentTime The current time. 404 * @return The clock sequence. 405 */ 406 private static final long getClockSequence( final long currentTime ) 407 { 408 final long clockSeq; 409 try( final var ignored = m_ClockSeqGuard.lock() ) 410 { 411 if( m_ClockSeq == Long.MIN_VALUE ) 412 { 413 //---* Initialise the clock sequence *------------------------- 414 m_ClockSeq = abs( getRandom().nextLong() ); 415 } 416 else if( currentTime <= m_LastClockSeqRequest ) 417 { 418 //noinspection NonAtomicOperationOnVolatileField 419 ++m_ClockSeq; 420 } 421 m_LastClockSeqRequest = currentTime; 422 clockSeq = m_ClockSeq; 423 } 424 425 final var retValue = (clockSeq & 0x0000000000003FFFL) << 48; 426 427 //---* Done *---------------------------------------------------------- 428 return retValue; 429 } // getClockSequence() 430 431 /** 432 * Returns the UUID for the namespace with the given name. 433 * 434 * @param key The name of the namespace. 435 * @return The UUID for the namespace, or {@null} if that namespace 436 * does not exist. 437 */ 438 @API( status = STABLE, since = "0.0.5" ) 439 public static final UUID getNamespaceUUID( final String key ) { return m_Namespaces.get( requireNotEmptyArgument( key, "key" ) ); } 440 441 /** 442 * Returns the names of the known UUID namespaces. 443 * 444 * @return The names of the namespaces. 445 */ 446 @SuppressWarnings( "unused" ) 447 @API( status = STABLE, since = "0.0.5" ) 448 public static final String [] listNamespaces() 449 { 450 final var retValue = m_Namespaces.keySet().toArray( EMPTY_String_ARRAY ); 451 452 //---* Done *---------------------------------------------------------- 453 return retValue; 454 } // listNamespaces() 455 456 /** 457 * Static factory to retrieve a type 3 (name based, MD5 hashed) or a 458 * type 5 (name based, SHA hashed) UUID based on the specified byte 459 * array.<br> 460 * <br>This method will always return the same output if the input is the 461 * same.<br> 462 * <br>The provided name should be prepended with the UUID for a 463 * designated name space, although this is neither enforced nor checked by 464 * this method. 465 * 466 * @param name A byte array to be used to construct a UUID. 467 * @param hashType The hash type to use. 468 * @return The UUID generated from the specified array. 469 * 470 * @see UUID#nameUUIDFromBytes(byte[]) 471 */ 472 @SuppressWarnings( {"MagicNumber", "ImplicitNumericConversion"} ) 473 @API( status = STABLE, since = "0.0.5" ) 474 public static final UUID nameUUIDFromBytes( final byte [] name, final HashType hashType ) 475 { 476 requireNonNullArgument( name, "name" ); 477 478 final var retValue = switch( requireNonNullArgument( hashType, "hashType" ) ) 479 { 480 case HASH_MD5 -> UUID.nameUUIDFromBytes( name ); 481 case HASH_SHA -> { 482 final var shaBytes = calculateSHA1Hash( name ); 483 shaBytes[6] &= 0x0f; // Clear version 484 shaBytes[6] |= 0x50; // Set to version 5 485 shaBytes[8] &= 0x3f; // Clear variant 486 //noinspection lossy-conversions 487 shaBytes[8] |= 0x80; // Set to IETF variant 488 489 var mostSigBits = 0L; 490 var leastSigBits = 0L; 491 for( var i = 0; i < 8; ++i ) 492 { 493 mostSigBits |= ((long) (shaBytes[7 - i] & 0xff)) << (i << 3); 494 leastSigBits |= ((long) (shaBytes[15 - i] & 0xff)) << (i << 3); 495 } 496 yield new UUID( mostSigBits, leastSigBits ); 497 } 498 default -> throw new UnsupportedEnumError( hashType ); 499 }; 500 501 //---* Done *---------------------------------------------------------- 502 return retValue; 503 } // nameUUIDFromBytes() 504 505 /** 506 * Creates a name-based (version type 3 or type 5, depending on 507 * the provided hash type) UUID from the given String.<br> 508 * <br>This method will always return the same output if the input is the 509 * same.<br> 510 * <br>The provided name should be prepended with the UUID for a 511 * designated name space, although this is neither enforced nor checked by 512 * this method. 513 * 514 * @param name The name base for the UUID. 515 * @param hashType The hash type to use. 516 * @return The UUID. 517 * 518 * @see UUID#nameUUIDFromBytes(byte[]) 519 */ 520 @API( status = STABLE, since = "0.0.5" ) 521 public static final UUID nameUUIDFromString( final CharSequence name, final HashType hashType ) 522 { 523 //---* Get the byte array *-------------------------------------------- 524 final var bytes = requireNonNullArgument( name, "name" ).toString().getBytes( UTF8 ); 525 526 //---* Create the UUID *----------------------------------------------- 527 final var retValue = nameUUIDFromBytes( bytes, hashType ); 528 529 //---* Done *---------------------------------------------------------- 530 return retValue; 531 } // nameUUIDFromString() 532 533 /** 534 * Creates a name-based (version type 3 or type 5, depending on 535 * the provided hash type) UUID from the given String, using the 536 * provided namespace UUID as the prefix.<br> 537 * <br>This method will always return the same output if the input is the 538 * same. 539 * 540 * @param namespace The UUID for the namespace. 541 * @param hashType The hash type to use. 542 * @param name The name base for the UUID. 543 * @return The UUID. 544 * 545 * @see UUID#nameUUIDFromBytes(byte[]) 546 */ 547 @API( status = STABLE, since = "0.0.5" ) 548 public static final UUID nameUUIDFromString( final UUID namespace, final CharSequence name, final HashType hashType ) 549 { 550 final var namespaceName = requireNonNullArgument( namespace, "namespace" ).toString() + requireNonNullArgument( name, "name" ); 551 552 //---* Create the UUID *----------------------------------------------- 553 final var retValue = nameUUIDFromBytes( namespaceName.getBytes( UTF8 ), hashType ); 554 555 //---* Done *---------------------------------------------------------- 556 return retValue; 557 } // nameUUIDFromString() 558 559 /** 560 * Static factory to retrieve a type 4 (pseudo randomly generated) UUID. 561 * The UUID is generated using a cryptographically strong pseudo random 562 * number generator.<br> 563 * <br>This is a wrapper for the method with the same name from 564 * {@link UUID}. 565 * 566 * @return A randomly generated UUID. 567 * 568 * @see UUID#randomUUID() 569 */ 570 @API( status = STABLE, since = "0.0.5" ) 571 public static final UUID randomUUID() { return UUID.randomUUID(); } 572 573 /** 574 * <p>{@summary Creates a sequence UUID from the given values; this UUID 575 * will have the type 0 (that is not officially defined).}</p> 576 * <p>UUIDs of this type are used to define globally identical keys, 577 * meaning that this method will always return the same output if the 578 * input is the same.</p> 579 * 580 * @param mostSignificant The most significant bits for the new UUID. 581 * @param leastSignificant The least significant bits for the new 582 * UUID. 583 * @return The new UUID of type 0. 584 */ 585 @API( status = STABLE, since = "0.0.5" ) 586 public static final UUID sequenceUUID( final long mostSignificant, final long leastSignificant ) 587 { 588 //---* Calculate the most significant bits *--------------------------- 589 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 590 final var timeLow = ( (mostSignificant << 44) & 0xFFFFF00000000000L) | ( (leastSignificant >> 20) & 0x00000FFF00000000L); 591 final var timeMid = (mostSignificant >> 8) & 0x00000000FFFF0000L; 592 final var timeHi = (mostSignificant >> 24) & 0x0000000000000FFFL; 593 final var mostSigBits = timeLow | timeMid | timeHi; 594 595 //---* Calculate the least significant bits *-------------------------- 596 final var variant = (0x2L << 62) & 0x8000000000000000L; 597 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 598 final var leastSigBits = variant | ( (mostSignificant & 0x03FF000000000000L) << 4) | (leastSignificant & 0x000FFFFFFFFFFFFFL); 599 600 //---* Create the UUID *----------------------------------------------- 601 final var retValue = new UUID( mostSigBits, leastSigBits ); 602 603 //---* Done *---------------------------------------------------------- 604 return retValue; 605 } // sequenceUUID() 606 607 /** 608 * Creates a time-based (version type 1) UUID, using the given node id. 609 * 610 * @param nodeId The node id; only the lower 48 bit from this value are 611 * used for the UUID. 612 * @return The UUID. 613 */ 614 @API( status = STABLE, since = "0.0.5" ) 615 public static final UUID timebasedUUID( final long nodeId ) 616 { 617 //---* Calculate the most significant bits *--------------------------- 618 final var currentTime = currentTimeNanos().divide( ONE_HUNDRED ).longValue(); 619 final var timeLow = (currentTime << 32) & 0xFFFFFFFF00000000L; 620 final var timeMid = (currentTime >> 16) & 0x00000000FFFF0000L; 621 final var version = 4096L; //(1 << 12) & 0x000000000000F000L; 622 final var timeHi = (currentTime >> 48) & 0x0000000000000FFFL; 623 final var mostSigBits = timeLow | timeMid | version | timeHi; 624 625 //---* Calculate the least significant bits *-------------------------- 626 final var variant = (0x2L << 62) & 0x8000000000000000L; 627 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 628 final var leastSigBits = variant | getClockSequence( currentTime ) | (nodeId & 0x0000FFFFFFFFFFFFL); 629 630 //---* Create the UUID *----------------------------------------------- 631 final var retValue = new UUID( mostSigBits, leastSigBits ); 632 633 //---* Done *---------------------------------------------------------- 634 return retValue; 635 } // timebasedUUID() 636 637 /** 638 * Creates a time-based (version type 1) UUID using the internal node id. 639 * 640 * @return The UUID. 641 * 642 * @see #m_NodeId 643 * @see #m_UsePseudoNodeId 644 */ 645 @API( status = STABLE, since = "0.0.5" ) 646 public static final UUID timebasedUUID() 647 { 648 //---* Create the UUID *----------------------------------------------- 649 final var retValue = timebasedUUID( m_NodeId ); 650 651 //---* Done *---------------------------------------------------------- 652 return retValue; 653 } // timebasedUUID() 654 655 /** 656 * Creates a time-based (version type 1) UUID from a dummy node id. 657 * 658 * @return The UUID. 659 */ 660 @SuppressWarnings( "unused" ) 661 @API( status = STABLE, since = "0.0.7" ) 662 public static final UUID timebasedUUIDFromDummyNode() 663 { 664 //---* Create the UUID *----------------------------------------------- 665 final var retValue = timebasedUUID( m_DummyNodeId ); 666 667 //---* Done *---------------------------------------------------------- 668 return retValue; 669 } // timebasedUUIDFromDummyNode() 670 671 /** 672 * Creates a time-based (version type 1) UUID from the given node 673 * name.<br> 674 * <br>The provided node name will be hashed (using MD5), the bytes from 675 * the result will be converted into 676 * {@link BigInteger}. Then 677 * {@link #timebasedUUID(long)} 678 * is called with the result from 679 * {@link BigInteger#longValue()}, 680 * called on the value mentioned before. 681 * 682 * @param nodeName The node name. 683 * @return The UUID. 684 */ 685 @API( status = STABLE, since = "0.0.5" ) 686 public static final UUID timebasedUUIDFromNodeName( final CharSequence nodeName ) 687 { 688 //---* Convert the node name to a numerical node id *------------------ 689 final var nodeId = new BigInteger( calculateMD5Hash( requireNonNullArgument( nodeName, "nodeName" ).toString().getBytes( UTF8 ) ) ); 690 691 //---* Create the UUID *----------------------------------------------- 692 final var retValue = timebasedUUID( nodeId.longValue() ); 693 694 //---* Done *---------------------------------------------------------- 695 return retValue; 696 } // timebasedUUIDFromNodeName() 697 698 /** 699 * Converts a UUID to a String that can be used as an XML id. 700 * 701 * @param input The UUID. 702 * @return The XML safe id. 703 */ 704 @API( status = STABLE, since = "0.3.0" ) 705 public static final String toXMLId( final UUID input ) 706 { 707 final var radix = m_UUIDXMLDigits [0].length - 1; 708 final var numbers = new long [] {requireNonNullArgument( input, "input" ).getMostSignificantBits(), input.getLeastSignificantBits()}; 709 final var buffer = new StringBuilder(); 710 for( final var number : numbers ) 711 { 712 if( isNotEmpty( buffer ) ) buffer.append( '-' ); 713 for( final var c : Long.toString( number, radix ).toUpperCase( ROOT ).toCharArray() ) 714 { 715 IntStream.range( 0, m_UUIDXMLDigits[0].length ) 716 .filter( index -> m_UUIDXMLDigits[0][index] == c ) 717 .findFirst() 718 .ifPresent( index -> buffer.append( m_UUIDXMLDigits[1][index] ) ); 719 } 720 } 721 722 final var retValue = buffer.toString(); 723 724 //---* Done *---------------------------------------------------------- 725 return retValue; 726 } // toXMLId() 727 728 /** 729 * <p>{@summary Creates a 730 * {@link UUID} 731 * from the given number (more precise, the 732 * given 733 * {@link BigInteger}).}</p> 734 * 735 * @param value The number. 736 * @return The UUID. 737 */ 738 public static final UUID uuidFromNumber( final BigInteger value ) 739 { 740 final var leastSignificantBits = requireNonNullArgument( value, "value" ).and( BIT_MASK ).longValue(); 741 final var mostSignificantBits = value.shiftRight( Long.SIZE ).and( BIT_MASK ).longValue(); 742 final var retValue = new UUID( mostSignificantBits, leastSignificantBits ); 743 744 //---* Done *---------------------------------------------------------- 745 return retValue; 746 } // uuidFromNumber() 747 748 /** 749 * <p>{@summary Creates a UUID from the string standard 750 * representation.}</p> 751 * <p>The UUID string representation is as described by this BNF:</p> 752 * <pre> 753 * UUID = <time_low> "-" <time_mid> "-" 754 * <time_high_and_version> "-" 755 * <variant_and_sequence> "-" 756 * <node> 757 * time_low = 4 × <hexOctet> 758 * time_mid = 2 × <hexOctet> 759 * time_high_and_version = 2 × <hexOctet> 760 * variant_and_sequence = 2 × <hexOctet> 761 * node = 6 × <hexOctet> 762 * hexOctet = <hexDigit><hexDigit> 763 * hexDigit = 764 * "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 765 * | "a" | "b" | "c" | "d" | "e" | "f" 766 * | "A" | "B" | "C" | "D" | "E" | "F" 767 * </pre> 768 * 769 * @param uuid The UUID string representation. 770 * @return The UUID from the given String representation. 771 * @throws NullArgumentException The argument is {@null}. 772 * @throws EmptyArgumentException The argument is the empty String. 773 * @throws IllegalArgumentException The argument is invalid. 774 * 775 * @see UUID#fromString(String) 776 * @see UUID#toString() 777 */ 778 @API( status = STABLE, since = "0.0.5" ) 779 public static final UUID uuidFromString( final CharSequence uuid ) throws IllegalArgumentException, EmptyArgumentException, NullArgumentException 780 { 781 final var retValue = fromString( requireNotEmptyArgument( uuid, "uuid" ).toString() ); 782 783 //---* Done *---------------------------------------------------------- 784 return retValue; 785 } // uuidFromString() 786 787 /** 788 * Returns a number (more precise, an instance of 789 * {@link BigInteger}) 790 * that represents the given UUID. 791 * 792 * @param uuid The UUID to convert. 793 * @return The number that represents the UUID. 794 * 795 * @since 0.1.0 796 */ 797 @API( status = STABLE, since = "0.1.0" ) 798 public static final BigInteger uuidToNumber( final UUID uuid ) 799 { 800 final var lsb = requireNonNullArgument( uuid, "uuid" ).getLeastSignificantBits(); 801 final var msb = uuid.getMostSignificantBits(); 802 var s1 = toBinaryString( lsb ); 803 s1 = repeat( "0", Long.SIZE - s1.length()) + s1; 804 var s2 = toBinaryString( msb ); 805 s2 = repeat( "0", Long.SIZE - s2.length()) + s2; 806 final var retValue = new BigInteger( s2 + s1, 2 ); 807 808 //---* Done *---------------------------------------------------------- 809 return retValue; 810 } // uuidToNumber() 811 812 /** 813 * Creates a time-based (version type 7) UUID. 814 * 815 * @return The UUID. 816 */ 817 @API( status = STABLE, since = "0.1.0" ) 818 public static final UUID version7UUID() 819 { 820 final var random = getRandom(); 821 822 //---* Calculate the most significant bits *-------------------------- 823 final long randA; 824 final long currentTime; 825 synchronized( m_UUID7Counter ) 826 { 827 randA = (long) m_UUID7Counter.getAndIncrement() & 0x0000000000000FFFL; 828 if( randA == 0 ) repose( 1 ); 829 currentTime = currentTimeMillis() << 16; 830 } 831 final var version = 28672L; //(0x07L << 12) & 0x000000000000F000L; 832 final var mostSigBits = currentTime | version | randA; 833 834 //---* Calculate the least significant bits *-------------------------- 835 final var variant = (0x02L << 62) & 0x8000000000000000L; 836 final var randB = (random.nextLong() << 32) & 0x3FFFFFFF00000000L; 837 final var randC = random.nextLong() & 0x00000000FFFFFFFFL; 838 final var leastSigBits = variant | randB | randC; 839 840 //---* Create the UUID *----------------------------------------------- 841 final var retValue = new UUID( mostSigBits, leastSigBits ); 842 843 //---* Done *---------------------------------------------------------- 844 return retValue; 845 } // version7UUID() 846} 847// class UniqueIdUtils 848 849/* 850 * End of File 851 */