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&nbsp;4122. It extends the
067 *  capabilities of the Java Runtime class
068 *  {@link UUID}.</p>
069 *
070 *  <h2>RFC&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;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>&#47;* Name string is a fully-qualified domain name *&#47;
118 *   uuid_t NameSpace_DNS = { &#47;* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 *&#47;
119 *       0x6ba7b810,
120 *       0x9dad,
121 *       0x11d1,
122 *       0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
123 *   };
124 *
125 *   &#47;* Name string is a URL *&#47;
126 *   uuid_t NameSpace_URL = { &#47;* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 *&#47;
127 *       0x6ba7b811,
128 *       0x9dad,
129 *       0x11d1,
130 *       0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
131 *   };
132 *
133 *   &#47;* Name string is an ISO OID *&#47;
134 *   uuid_t NameSpace_OID = { &#47;* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 *&#47;
135 *       0x6ba7b812,
136 *       0x9dad,
137 *       0x11d1,
138 *       0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
139 *   };
140 *
141 *   &#47;* Name string is an X.500 DN (in DER or a text output format) *&#47;
142 *   uuid_t NameSpace_X500 = { &#47;* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 *&#47;
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     *  &quot;{@value #PROPERTY_USE_PSEUDO_NODE_ID}&quot;.<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&nbsp;3 (name based, MD5 hashed) or a
458     *  type&nbsp;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&nbsp;3 or type&nbsp;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&nbsp;3 or type&nbsp;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                   = &lt;time_low&gt; "-" &lt;time_mid&gt; "-"
754     *                           &lt;time_high_and_version&gt; "-"
755     *                           &lt;variant_and_sequence&gt; "-"
756     *                           &lt;node&gt;
757     *  time_low               = 4 &times; &lt;hexOctet&gt;
758     *  time_mid               = 2 &times; &lt;hexOctet&gt;
759     *  time_high_and_version  = 2 &times; &lt;hexOctet&gt;
760     *  variant_and_sequence   = 2 &times; &lt;hexOctet&gt;
761     *  node                   = 6 &times; &lt;hexOctet&gt;
762     *  hexOctet               = &lt;hexDigit&gt;&lt;hexDigit&gt;
763     *  hexDigit               =
764     *        &quot;0&quot; | &quot;1&quot; | &quot;2&quot; | &quot;3&quot; | &quot;4&quot; | &quot;5&quot; | &quot;6&quot; | &quot;7&quot; | &quot;8&quot; | &quot;9&quot;
765     *      | &quot;a&quot; | &quot;b&quot; | &quot;c&quot; | &quot;d&quot; | &quot;e&quot; | &quot;f&quot;
766     *      | &quot;A&quot; | &quot;B&quot; | &quot;C&quot; | &quot;D&quot; | &quot;E&quot; | &quot;F&quot;
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 */