/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.id;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.Serializable;
/**
* A 128-bit value to serve as a universally unique identifier. Two
* <code>Uuid</code>s are equal if they have the same 128-bit value.
* <code>Uuid</code> instances can be created using the static methods
* of the {@link UuidFactory} class.
*
* <p>The design of this class is intended to support the use of
* universally unique identifiers that
*
* <ol>
* <li>have a high likelihood of uniqueness over space and time and
* <li>are computationally difficult to guess.
* </ol>
*
* The second goal is intended to support the treatment of data
* containing a <code>Uuid</code> as a capability. Note that not all
* defined <code>Uuid</code> values imply a generation algorithm that
* supports this goal.
*
* <p>The most significant 64 bits of the value can be decomposed into
* unsigned integer fields according to the following bit masks:
*
* <pre>
* 0xFFFFFFFF00000000 time_low
* 0x00000000FFFF0000 time_mid
* 0x000000000000F000 version
* 0x0000000000000FFF time_hi
* </pre>
*
* <p>The least significant 64 bits of the value can be decomposed
* into unsigned integer fields according to the following bit masks:
*
* <pre>
* 0xC000000000000000 variant
* 0x3FFF000000000000 clock_seq
* 0x0000FFFFFFFFFFFF node
* </pre>
*
* <p>This specification defines the meaning (and implies aspects of
* the generation algorithm) of <code>Uuid</code> values if the
* variant field is <code>0x2</code> and the <code>version</code>
* field is either <code>0x1</code> or <code>0x4</code>.
*
* <p>If the <code>version</code> field is <code>0x1</code>, then
*
* <ul>
*
* <li>the <code>time_low</code>, <code>time_mid</code>, and
* <code>time_hi</code> fields are the least, middle, and most
* significant bits (respectively) of a 60-bit timestamp of
* 100-nanosecond intervals since midnight, October 15, 1582 UTC,
*
* <li>the <code>clock_seq</code> field is a 14-bit number chosen to
* help avoid duplicate <code>Uuid</code> values in the event of a
* changed node address or a backward system clock adjustment (such as
* a random number when in doubt, or the previously used number
* incremented by one if just a backward clock adjustment is
* detected), and
*
* <li>the <code>node</code> field is an IEEE 802 address (a 48-bit
* value).
*
* <p>As an alternative to an IEEE 802 address (such as if one is not
* available to the generation algorithm), the <code>node</code> field
* may also be a 48-bit number for which the most significant bit is
* set to <code>1</code> and the remaining bits were produced from a
* cryptographically strong random sequence.
*
* </ul>
*
* <p>If the <code>version</code> field is <code>0x4</code>, then the
* <code>time_low</code>, <code>time_mid</code>, <code>time_hi</code>,
* <code>clock_seq</code>, and <code>node</code> fields are values
* that were produced from a cryptographically strong random sequence.
*
* <p>Only <code>Uuid</code> values with a <code>version</code> field
* of <code>0x4</code> are considered computationally difficult to
* guess. A <code>Uuid</code> value with a <code>version</code> field
* of <code>0x1</code> should not be treated as a capability.
*
* <p>A subclass of <code>Uuid</code> must not implement {@link
* Externalizable}; this restriction is enforced by this class's
* constructor and <code>readObject</code> methods.
*
* @author Sun Microsystems, Inc.
* @since 2.0
**/
public class Uuid implements Serializable {
private static final long serialVersionUID = -106268922535833151L;
/**
* The most significant 64 bits of the 128-bit value.
*
* @serial
**/
private final long bits0;
/**
* The least significant 64 bits of the 128-bit value.
*
* @serial
**/
private final long bits1;
/**
* Creates a new <code>Uuid</code> with the specified 128-bit
* value.
*
* @param bits0 the most significant 64 bits of the 128-bit value
*
* @param bits1 the least significant 64 bits of the 128-bit value
*
* @throws SecurityException if the class of this object
* implements <code>Externalizable</code>
**/
protected Uuid(long bits0, long bits1) {
if (!isValid()) {
throw new SecurityException("invalid class: " +
this.getClass().getName());
}
this.bits0 = bits0;
this.bits1 = bits1;
}
/**
* Returns the most significant 64 bits of this
* <code>Uuid</code>'s 128-bit value.
*
* @return the most significant 64 bits of the 128-bit value
**/
public final long getMostSignificantBits() {
return bits0;
}
/**
* Returns the least significant 64 bits of this
* <code>Uuid</code>'s 128-bit value.
*
* @return the least significant 64 bits of the 128-bit value
**/
public final long getLeastSignificantBits() {
return bits1;
}
/**
* Returns the hash code value for this <code>Uuid</code>.
*
* @return the hash code value for this <code>Uuid</code>
**/
public final int hashCode() {
return (int) ((bits0 >>> 32) ^ bits0 ^ (bits1 >>> 32) ^ bits1);
}
/**
* Compares the specified object with this <code>Uuid</code> for
* equality.
*
* This method returns <code>true</code> if and only if the
* specified object is a <code>Uuid</code> instance with the same
* 128-bit value as this one.
*
* @param obj the object to compare this <code>Uuid</code> to
*
* @return <code>true</code> if the given object is equivalent to
* this one, and <code>false</code> otherwise
**/
public final boolean equals(Object obj) {
if (obj instanceof Uuid) {
Uuid other = (Uuid) obj;
return bits0 == other.bits0 && bits1 == other.bits1;
} else {
return false;
}
}
/**
* Returns a string representation of this <code>Uuid</code>.
*
* <p>The string representation is 36 characters long, with five
* fields of zero-filled, lowercase hexadecimal numbers separated
* by hyphens. The fields of the string representation are
* derived from the components of the 128-bit value in the
* following order:
*
* <ul>
*
* <li><code>time_low</code> (8 hexadecimal digits)
*
* <li><code>time_mid</code> (4 hexadecimal digits)
*
* <li><code>version</code> and <code>time_hi</code> treated as a
* single field (4 hexadecimal digits)
*
* <li><code>variant</code> and <code>clock_seq</code> treated as
* a single field (4 hexadecimal digits)
*
* <li><code>node</code> (12 hexadecimal digits)
*
* </ul>
*
* <p>As an example, a <code>Uuid</code> with the 128-bit value
*
* <pre>0x0123456789ABCDEF0123456789ABCDEF</pre>
*
* would have the following string representation:
*
* <pre>01234567-89ab-cdef-0123-456789abcdef</pre>
*
* @return a string representation of this <code>Uuid</code>
**/
public final String toString() {
return
toHexString(bits0 >>> 32, 8) + "-" +
toHexString(bits0 >>> 16, 4) + "-" +
toHexString(bits0 >>> 0, 4) + "-" +
toHexString(bits1 >>> 48, 4) + "-" +
toHexString(bits1 >>> 0, 12);
}
/**
* Returns the specified number of the least significant digits of
* the hexadecimal representation of the given value, discarding
* more significant digits or padding with zeros as necessary.
* Only lowercase letters are used in the returned hexadecimal
* representation.
**/
private String toHexString(long value, int digits) {
long cutoff = 1L << (digits * 4);
return Long.toHexString(cutoff | (value & (cutoff - 1))).substring(1);
}
/**
* Marshals a binary representation of this <code>Uuid</code> to
* an <code>OutputStream</code>.
*
* <p>Specifically, this method writes the 128-bit value to the
* stream as 16 bytes in network (big-endian) byte order.
*
* @param out the <code>OutputStream</code> to write this
* <code>Uuid</code> to
*
* @throws IOException if an I/O exception occurs while performing
* this operation
*
* @throws NullPointerException if <code>out</code> is
* <code>null</code>
**/
public final void write(OutputStream out) throws IOException {
writeLong(bits0, out);
writeLong(bits1, out);
}
/**
* Write a long value to an OutputStream in big-endian byte order.
**/
private static void writeLong(long value, OutputStream out)
throws IOException
{
out.write((int) (value >>> 56) & 0xFF);
out.write((int) (value >>> 48) & 0xFF);
out.write((int) (value >>> 40) & 0xFF);
out.write((int) (value >>> 32) & 0xFF);
out.write((int) (value >>> 24) & 0xFF);
out.write((int) (value >>> 16) & 0xFF);
out.write((int) (value >>> 8) & 0xFF);
out.write((int) (value >>> 0) & 0xFF);
}
/**
* Delegates to the superclass's {@link Object#finalize finalize}
* method. This method prevents a subclass from declaring an
* overriding <code>finalize</code> method.
*
* @throws Throwable if the superclass's <code>finalize</code>
* method throws a <code>Throwable</code>
**/
protected final void finalize() throws Throwable { }
/**
* Returns this object. This method prevents a subclass from
* declaring a <code>writeReplace</code> method with an alternate
* implementation.
*
* @return this object
**/
protected final Object writeReplace() {
return this;
}
/**
* Returns this object. This method prevents a subclass from
* declaring a <code>readResolve</code> method with an alternate
* implementation.
*
* @return this object
**/
protected final Object readResolve() {
return this;
}
/**
* @throws InvalidObjectException if the class of this object
* implements <code>Externalizable</code>
**/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
if (!isValid()) {
throw new InvalidObjectException("invalid class: " +
this.getClass().getName());
}
in.defaultReadObject();
}
/**
* @throws InvalidObjectException unconditionally
**/
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("no data in stream; class: " +
this.getClass().getName());
}
private boolean isValid() {
return !(this instanceof Externalizable);
}
}