001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 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 * <pre><code> /* 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 * };</code></pre> 148 * 149 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 150 * @version $Id: UniqueIdUtils.java 1111 2024-03-04 21:54:29Z 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 1111 2024-03-04 21:54:29Z 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 {@code true} means that only pseudo ids will be used 206 * throughout the current run of the program, while {@code 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 private static final long m_DummyNodeId; 282 283 /** 284 * The UUIDs for the predefined name spaces, according to RFC 4122. 285 */ 286 @SuppressWarnings( "StaticCollection" ) 287 private static final Map<String,UUID> m_Namespaces; 288 289 /** 290 * The node id that is used to generate UUIDs. This is either the MAC 291 * address of one of the NICs in the current system, or a random value. 292 */ 293 private static final long m_NodeId; 294 295 /** 296 * This flag controls if 297 * {@link #m_NodeId} 298 * is forced to be a random value.<br> 299 * <br>It will be controlled by the system property 300 * "{@value #PROPERTY_USE_PSEUDO_NODE_ID}".<br> 301 * <br>Using a pseudo node id would generate anonymous UUIDs. 302 */ 303 private static final boolean m_UsePseudoNodeId; 304 305 /** 306 * The max UUID. 307 */ 308 @API( status = STABLE, since = "0.0.5" ) 309 public static final UUID UUID_MAX; 310 311 /** 312 * The nil UUID. 313 */ 314 @API( status = STABLE, since = "0.0.5" ) 315 public static final UUID UUID_NIL; 316 317 static 318 { 319 //---* Create the locks *---------------------------------------------- 320 m_ClockSeqGuard = AutoLock.of(); 321 322 //---* Create the dummy node id *-------------------------------------- 323 m_DummyNodeId = createPseudoNodeId(); 324 325 //---* Get the flag that controls the generation of the node id *------ 326 m_UsePseudoNodeId = Boolean.getBoolean( PROPERTY_USE_PSEUDO_NODE_ID ); 327 328 //---* Retrieve the node id *------------------------------------------ 329 m_NodeId = m_UsePseudoNodeId ? createPseudoNodeId() : getNodeId(); 330 331 //---* Create the namespaces *----------------------------------------- 332 final Map<String,UUID> namespaces = new TreeMap<>(); 333 334 namespaces.put( "DNS", fromString( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) ); 335 namespaces.put( "URL", fromString( "6ba7b811-9dad-11d1-80b4-00c04fd430c8" ) ); 336 namespaces.put( "ISO_OID", fromString( "6ba7b812-9dad-11d1-80b4-00c04fd430c8" ) ); 337 namespaces.put( "X500", fromString( "6ba7b814-9dad-11d1-80b4-00c04fd430c8" ) ); 338 339 var internalNamespace = "tquadrat"; 340 namespaces.put( internalNamespace, UUID.nameUUIDFromBytes( internalNamespace.getBytes( UTF8 ) ) ); 341 internalNamespace = "Foundation"; 342 namespaces.put( internalNamespace, UUID.nameUUIDFromBytes( internalNamespace.getBytes( UTF8 ) ) ); 343 344 m_Namespaces = Map.copyOf( namespaces ); 345 346 //---* The UUIDs *----------------------------------------------------- 347 UUID_NIL = new UUID( 0, 0 ); 348 UUID_MAX = new UUID( 0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL ); 349 } 350 351 /*---------*\ 352 ====** Methods **========================================================== 353 \*---------*/ 354 /** 355 * Converts an XML safe id that was created through 356 * {@link #toXMLId(UUID)} 357 * back to a UUID. 358 * 359 * @param input The XML safe id. 360 * @return The UUID. 361 * @throws IllegalArgumentException The given XML safe id cannot be 362 * converted to a UUID. 363 */ 364 @API( status = STABLE, since = "0.3.0" ) 365 public static final UUID fromXMLId( final CharSequence input ) 366 { 367 final var radix = m_UUIDXMLDigits [0].length - 1; 368 final var numbers = new long [2]; 369 370 final var parts = splitString( requireNotBlankArgument( input, "input" ).toString().toUpperCase( ROOT ), "-" ); 371 final var message = "Cannot convert '%s' to a UUID!".formatted( input ); 372 requireValidIntegerArgument( parts.length, "input", length -> length == 2, $ -> message ); 373 for( var i = 0; i < parts.length; ++i ) 374 { 375 final var buffer = new StringBuilder(); 376 for( final var c : parts [i].toUpperCase( ROOT ).toCharArray() ) 377 { 378 IntStream.range( 0, m_UUIDXMLDigits[1].length ) 379 .filter( index -> m_UUIDXMLDigits[1][index] == c ) 380 .findFirst() 381 .ifPresentOrElse( index -> buffer.append( m_UUIDXMLDigits[0][index] ), () -> {throw new IllegalArgumentException( message );} ); 382 } 383 numbers [i] = Long.parseLong( buffer.toString().toLowerCase( ROOT ), radix ); 384 } 385 386 final var retValue = new UUID( numbers [0], numbers [1] ); 387 388 //---* Done *---------------------------------------------------------- 389 return retValue; 390 } // fromXMLId() 391 392 /** 393 * <p>{@summary Returns the clock sequence.} It will be initialised with a 394 * random number on each time the program starts, and it remains unchanged 395 * until the system detects a clock shift; in that case, it will be 396 * increased by one.</p> 397 * <p>An overflow for the clock sequence is possible, but does not harm.</p> 398 * 399 * @param currentTime The current time. 400 * @return The clock sequence. 401 */ 402 private static final long getClockSequence( final long currentTime ) 403 { 404 final long clockSeq; 405 try( final var ignored = m_ClockSeqGuard.lock() ) 406 { 407 if( m_ClockSeq == Long.MIN_VALUE ) 408 { 409 //---* Initialise the clock sequence *------------------------- 410 m_ClockSeq = abs( getRandom().nextLong() ); 411 } 412 else if( currentTime <= m_LastClockSeqRequest ) 413 { 414 //noinspection NonAtomicOperationOnVolatileField 415 ++m_ClockSeq; 416 } 417 m_LastClockSeqRequest = currentTime; 418 clockSeq = m_ClockSeq; 419 } 420 421 final var retValue = (clockSeq & 0x0000000000003FFFL) << 48; 422 423 //---* Done *---------------------------------------------------------- 424 return retValue; 425 } // getClockSequence() 426 427 /** 428 * Returns the UUID for the namespace with the given name. 429 * 430 * @param key The name of the namespace. 431 * @return The UUID for the namespace, or {@code null} if that namespace 432 * does not exist. 433 */ 434 @API( status = STABLE, since = "0.0.5" ) 435 public static final UUID getNamespaceUUID( final String key ) { return m_Namespaces.get( requireNotEmptyArgument( key, "key" ) ); } 436 437 /** 438 * Returns the names of the known UUID namespaces. 439 * 440 * @return The names of the namespaces. 441 */ 442 @API( status = STABLE, since = "0.0.5" ) 443 public static final String [] listNamespaces() 444 { 445 final var retValue = m_Namespaces.keySet().toArray( EMPTY_String_ARRAY ); 446 447 //---* Done *---------------------------------------------------------- 448 return retValue; 449 } // listNamespaces() 450 451 /** 452 * Static factory to retrieve a type 3 (name based, MD5 hashed) or a 453 * type 5 (name based, SHA hashed) UUID based on the specified byte 454 * array.<br> 455 * <br>This method will always return the same output if the input is the 456 * same.<br> 457 * <br>The provided name should be prepended with the UUID for a 458 * designated name space, although this is neither enforced nor checked by 459 * this method. 460 * 461 * @param name A byte array to be used to construct a UUID. 462 * @param hashType The hash type to use. 463 * @return The UUID generated from the specified array. 464 * 465 * @see UUID#nameUUIDFromBytes(byte[]) 466 */ 467 @SuppressWarnings( {"MagicNumber", "ImplicitNumericConversion"} ) 468 @API( status = STABLE, since = "0.0.5" ) 469 public static final UUID nameUUIDFromBytes( final byte [] name, final HashType hashType ) 470 { 471 requireNonNullArgument( name, "name" ); 472 473 final var retValue = switch( requireNonNullArgument( hashType, "hashType" ) ) 474 { 475 case HASH_MD5 -> UUID.nameUUIDFromBytes( name ); 476 case HASH_SHA -> { 477 final var shaBytes = calculateSHA1Hash( name ); 478 shaBytes[6] &= 0x0f; // Clear version 479 shaBytes[6] |= 0x50; // Set to version 5 480 shaBytes[8] &= 0x3f; // Clear variant 481 //noinspection lossy-conversions 482 shaBytes[8] |= 0x80; // Set to IETF variant 483 484 var mostSigBits = 0L; 485 var leastSigBits = 0L; 486 for( var i = 0; i < 8; ++i ) 487 { 488 mostSigBits |= ((long) (shaBytes[7 - i] & 0xff)) << (i << 3); 489 leastSigBits |= ((long) (shaBytes[15 - i] & 0xff)) << (i << 3); 490 } 491 yield new UUID( mostSigBits, leastSigBits ); 492 } 493 default -> throw new UnsupportedEnumError( hashType ); 494 }; 495 496 //---* Done *---------------------------------------------------------- 497 return retValue; 498 } // nameUUIDFromBytes() 499 500 /** 501 * Creates a name-based (version type 3 or type 5, depending on 502 * the provided hash type) UUID from the given String.<br> 503 * <br>This method will always return the same output if the input is the 504 * same.<br> 505 * <br>The provided name should be prepended with the UUID for a 506 * designated name space, although this is neither enforced nor checked by 507 * this method. 508 * 509 * @param name The name base for the UUID. 510 * @param hashType The hash type to use. 511 * @return The UUID. 512 * 513 * @see UUID#nameUUIDFromBytes(byte[]) 514 */ 515 @API( status = STABLE, since = "0.0.5" ) 516 public static final UUID nameUUIDFromString( final CharSequence name, final HashType hashType ) 517 { 518 //---* Get the byte array *-------------------------------------------- 519 final var bytes = requireNonNullArgument( name, "name" ).toString().getBytes( UTF8 ); 520 521 //---* Create the UUID *----------------------------------------------- 522 final var retValue = nameUUIDFromBytes( bytes, hashType ); 523 524 //---* Done *---------------------------------------------------------- 525 return retValue; 526 } // nameUUIDFromString() 527 528 /** 529 * Creates a name-based (version type 3 or type 5, depending on 530 * the provided hash type) UUID from the given String, using the 531 * provided namespace UUID as the prefix.<br> 532 * <br>This method will always return the same output if the input is the 533 * same. 534 * 535 * @param namespace The UUID for the namespace. 536 * @param hashType The hash type to use. 537 * @param name The name base for the UUID. 538 * @return The UUID. 539 * 540 * @see UUID#nameUUIDFromBytes(byte[]) 541 */ 542 @API( status = STABLE, since = "0.0.5" ) 543 public static final UUID nameUUIDFromString( final UUID namespace, final CharSequence name, final HashType hashType ) 544 { 545 final var namespaceName = requireNonNullArgument( namespace, "namespace" ).toString() + requireNonNullArgument( name, "name" ); 546 547 //---* Create the UUID *----------------------------------------------- 548 final var retValue = nameUUIDFromBytes( namespaceName.getBytes( UTF8 ), hashType ); 549 550 //---* Done *---------------------------------------------------------- 551 return retValue; 552 } // nameUUIDFromString() 553 554 /** 555 * Static factory to retrieve a type 4 (pseudo randomly generated) UUID. 556 * The UUID is generated using a cryptographically strong pseudo random 557 * number generator.<br> 558 * <br>This is a wrapper for the method with the same name from 559 * {@link UUID}. 560 * 561 * @return A randomly generated UUID. 562 * 563 * @see UUID#randomUUID() 564 */ 565 @API( status = STABLE, since = "0.0.5" ) 566 public static final UUID randomUUID() { return UUID.randomUUID(); } 567 568 /** 569 * <p>{@summary Creates a sequence UUID from the given values; this UUID 570 * will have the type 0 (that is not officially defined).}</p> 571 * <p>UUIDs of this type are used to define globally identical keys, 572 * meaning that this method will always return the same output if the 573 * input is the same.</p> 574 * 575 * @param mostSignificant The most significant bits for the new UUID. 576 * @param leastSignificant The least significant bits for the new 577 * UUID. 578 * @return The new UUID of type 0. 579 */ 580 @API( status = STABLE, since = "0.0.5" ) 581 public static final UUID sequenceUUID( final long mostSignificant, final long leastSignificant ) 582 { 583 //---* Calculate the most significant bits *--------------------------- 584 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 585 final var timeLow = ( (mostSignificant << 44) & 0xFFFFF00000000000L) | ( (leastSignificant >> 20) & 0x00000FFF00000000L); 586 final var timeMid = (mostSignificant >> 8) & 0x00000000FFFF0000L; 587 final var timeHi = (mostSignificant >> 24) & 0x0000000000000FFFL; 588 final var mostSigBits = timeLow | timeMid | timeHi; 589 590 //---* Calculate the least significant bits *-------------------------- 591 final var variant = (0x2L << 62) & 0x8000000000000000L; 592 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 593 final var leastSigBits = variant | ( (mostSignificant & 0x03FF000000000000L) << 4) | (leastSignificant & 0x000FFFFFFFFFFFFFL); 594 595 //---* Create the UUID *----------------------------------------------- 596 final var retValue = new UUID( mostSigBits, leastSigBits ); 597 598 //---* Done *---------------------------------------------------------- 599 return retValue; 600 } // sequenceUUID() 601 602 /** 603 * Creates a time-based (version type 1) UUID, using the given node id. 604 * 605 * @param nodeId The node id; only the lower 48 bit from this value are 606 * used for the UUID. 607 * @return The UUID. 608 */ 609 @API( status = STABLE, since = "0.0.5" ) 610 public static final UUID timebasedUUID( final long nodeId ) 611 { 612 //---* Calculate the most significant bits *--------------------------- 613 final var currentTime = currentTimeNanos().divide( ONE_HUNDRED ).longValue(); 614 final var timeLow = (currentTime << 32) & 0xFFFFFFFF00000000L; 615 final var timeMid = (currentTime >> 16) & 0x00000000FFFF0000L; 616 final var version = 4096L; //(1 << 12) & 0x000000000000F000L; 617 final var timeHi = (currentTime >> 48) & 0x0000000000000FFFL; 618 final var mostSigBits = timeLow | timeMid | version | timeHi; 619 620 //---* Calculate the least significant bits *-------------------------- 621 final var variant = (0x2L << 62) & 0x8000000000000000L; 622 @SuppressWarnings( "OverlyComplexBooleanExpression" ) 623 final var leastSigBits = variant | getClockSequence( currentTime ) | (nodeId & 0x0000FFFFFFFFFFFFL); 624 625 //---* Create the UUID *----------------------------------------------- 626 final var retValue = new UUID( mostSigBits, leastSigBits ); 627 628 //---* Done *---------------------------------------------------------- 629 return retValue; 630 } // timebasedUUID() 631 632 /** 633 * Creates a time-based (version type 1) UUID using the internal node id. 634 * 635 * @return The UUID. 636 * 637 * @see #m_NodeId 638 * @see #m_UsePseudoNodeId 639 */ 640 @API( status = STABLE, since = "0.0.5" ) 641 public static final UUID timebasedUUID() 642 { 643 //---* Create the UUID *----------------------------------------------- 644 final var retValue = timebasedUUID( m_NodeId ); 645 646 //---* Done *---------------------------------------------------------- 647 return retValue; 648 } // timebasedUUID() 649 650 /** 651 * Creates a time-based (version type 1) UUID from a dummy node id. 652 * 653 * @return The UUID. 654 */ 655 @API( status = STABLE, since = "0.0.7" ) 656 public static final UUID timebasedUUIDFromDummyNode() 657 { 658 //---* Create the UUID *----------------------------------------------- 659 final var retValue = timebasedUUID( m_DummyNodeId ); 660 661 //---* Done *---------------------------------------------------------- 662 return retValue; 663 } // timebasedUUIDFromDummyNode() 664 665 /** 666 * Creates a time-based (version type 1) UUID from the given node 667 * name.<br> 668 * <br>The provided node name will be hashed (using MD5), the bytes from 669 * the result will be converted into 670 * {@link BigInteger}. Then 671 * {@link #timebasedUUID(long)} 672 * is called with the result from 673 * {@link BigInteger#longValue()}, 674 * called on the value mentioned before. 675 * 676 * @param nodeName The node name. 677 * @return The UUID. 678 */ 679 @API( status = STABLE, since = "0.0.5" ) 680 public static final UUID timebasedUUIDFromNodeName( final CharSequence nodeName ) 681 { 682 //---* Convert the node name to a numerical node id *------------------ 683 final var nodeId = new BigInteger( calculateMD5Hash( requireNonNullArgument( nodeName, "nodeName" ).toString().getBytes( UTF8 ) ) ); 684 685 //---* Create the UUID *----------------------------------------------- 686 final var retValue = timebasedUUID( nodeId.longValue() ); 687 688 //---* Done *---------------------------------------------------------- 689 return retValue; 690 } // timebasedUUIDFromNodeName() 691 692 /** 693 * Converts a UUID to a String that can be used as an XML id. 694 * 695 * @param input The UUID. 696 * @return The XML safe id. 697 */ 698 @API( status = STABLE, since = "0.3.0" ) 699 public static final String toXMLId( final UUID input ) 700 { 701 final var radix = m_UUIDXMLDigits [0].length - 1; 702 final var numbers = new long [] {requireNonNullArgument( input, "input" ).getMostSignificantBits(), input.getLeastSignificantBits()}; 703 final var buffer = new StringBuilder(); 704 for( final var number : numbers ) 705 { 706 if( isNotEmpty( buffer ) ) buffer.append( '-' ); 707 for( final var c : Long.toString( number, radix ).toUpperCase( ROOT ).toCharArray() ) 708 { 709 IntStream.range( 0, m_UUIDXMLDigits[0].length ) 710 .filter( index -> m_UUIDXMLDigits[0][index] == c ) 711 .findFirst() 712 .ifPresent( index -> buffer.append( m_UUIDXMLDigits[1][index] ) ); 713 } 714 } 715 716 final var retValue = buffer.toString(); 717 718 //---* Done *---------------------------------------------------------- 719 return retValue; 720 } // toXMLId() 721 722 /** 723 * <p>{@summary Creates a 724 * {@link UUID} 725 * from the given number (more precise, the 726 * given 727 * {@link BigInteger}).}</p> 728 * 729 * @param value The number. 730 * @return The UUID. 731 */ 732 public static final UUID uuidFromNumber( final BigInteger value ) 733 { 734 final var leastSignificantBits = requireNonNullArgument( value, "value" ).and( BIT_MASK ).longValue(); 735 final var mostSignificantBits = value.shiftRight( Long.SIZE ).and( BIT_MASK ).longValue(); 736 final var retValue = new UUID( mostSignificantBits, leastSignificantBits ); 737 738 //---* Done *---------------------------------------------------------- 739 return retValue; 740 } // uuidFromNumber() 741 742 /** 743 * <p>{@summary Creates a UUID from the string standard 744 * representation.}</p> 745 * <p>The UUID string representation is as described by this BNF:</p> 746 * <pre> 747 * UUID = <time_low> "-" <time_mid> "-" 748 * <time_high_and_version> "-" 749 * <variant_and_sequence> "-" 750 * <node> 751 * time_low = 4 × <hexOctet> 752 * time_mid = 2 × <hexOctet> 753 * time_high_and_version = 2 × <hexOctet> 754 * variant_and_sequence = 2 × <hexOctet> 755 * node = 6 × <hexOctet> 756 * hexOctet = <hexDigit><hexDigit> 757 * hexDigit = 758 * "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 759 * | "a" | "b" | "c" | "d" | "e" | "f" 760 * | "A" | "B" | "C" | "D" | "E" | "F" 761 * </pre> 762 * 763 * @param uuid The UUID string representation. 764 * @return The UUID from the given String representation. 765 * @throws NullArgumentException The argument is {@code null}. 766 * @throws EmptyArgumentException The argument is the empty String. 767 * @throws IllegalArgumentException The argument is invalid. 768 * 769 * @see UUID#fromString(String) 770 * @see UUID#toString() 771 */ 772 @API( status = STABLE, since = "0.0.5" ) 773 public static final UUID uuidFromString( final CharSequence uuid ) throws IllegalArgumentException, EmptyArgumentException, NullArgumentException 774 { 775 final var retValue = fromString( requireNotEmptyArgument( uuid, "uuid" ).toString() ); 776 777 //---* Done *---------------------------------------------------------- 778 return retValue; 779 } // uuidFromString() 780 781 /** 782 * Returns a number (more precise, an instance of 783 * {@link BigInteger}) 784 * that represents the given UUID. 785 * 786 * @param uuid The UUID to convert. 787 * @return The number that represents the UUID. 788 * 789 * @since 0.1.0 790 */ 791 @API( status = STABLE, since = "0.1.0" ) 792 public static final BigInteger uuidToNumber( final UUID uuid ) 793 { 794 final var lsb = requireNonNullArgument( uuid, "uuid" ).getLeastSignificantBits(); 795 final var msb = uuid.getMostSignificantBits(); 796 var s1 = toBinaryString( lsb ); 797 s1 = repeat( "0", Long.SIZE - s1.length()) + s1; 798 var s2 = toBinaryString( msb ); 799 s2 = repeat( "0", Long.SIZE - s2.length()) + s2; 800 final var retValue = new BigInteger( s2 + s1, 2 ); 801 802 //---* Done *---------------------------------------------------------- 803 return retValue; 804 } // uuidToNumber() 805 806 /** 807 * Creates a time-based (version type 7) UUID. 808 * 809 * @return The UUID. 810 */ 811 @API( status = STABLE, since = "0.1.0" ) 812 public static final UUID version7UUID() 813 { 814 final var random = getRandom(); 815 816 //---* Calculate the most significant bits *-------------------------- 817 final long randA; 818 final long currentTime; 819 synchronized( m_UUID7Counter ) 820 { 821 randA = (long) m_UUID7Counter.getAndIncrement() & 0x0000000000000FFFL; 822 if( randA == 0 ) repose( 1 ); 823 currentTime = currentTimeMillis() << 16; 824 } 825 final var version = 28672L; //(0x07L << 12) & 0x000000000000F000L; 826 final var mostSigBits = currentTime | version | randA; 827 828 //---* Calculate the least significant bits *-------------------------- 829 final var variant = (0x02L << 62) & 0x8000000000000000L; 830 final var randB = (random.nextLong() << 32) & 0x3FFFFFFF00000000L; 831 final var randC = random.nextLong() & 0x00000000FFFFFFFFL; 832 final var leastSigBits = variant | randB | randC; 833 834 //---* Create the UUID *----------------------------------------------- 835 final var retValue = new UUID( mostSigBits, leastSigBits ); 836 837 //---* Done *---------------------------------------------------------- 838 return retValue; 839 } // version7UUID() 840} 841// class UniqueIdUtils 842 843/* 844 * End of File 845 */