/**
* Copyright (C) 2001-2005 Connected Systems Inc. All rights reserved.
* This software is the confidential and proprietary information of
* Connected Systems Inc. ("Confidential Information").
*/
//package biz.evot.util.uuid;
import java.io.Serializable;
public class UUID implements Serializable, Cloneable, Comparable {
/**
*
*/
private static final long serialVersionUID = 112102158432121213L;
private final static String kHexChars = "0123456789abcdefABCDEF";
public final static byte INDEX_CLOCK_HI = 6;
public final static byte INDEX_CLOCK_MID = 4;
public final static byte INDEX_CLOCK_LO = 0;
public final static byte INDEX_TYPE = 6;
// Clock seq. & variant are multiplexed...
public final static byte INDEX_CLOCK_SEQUENCE = 8;
public final static byte INDEX_VARIATION = 8;
public final static byte TYPE_NULL = 0;
public final static byte TYPE_TIME_BASED = 1;
public final static byte TYPE_DCE = 2; // Not
// used
public final static byte TYPE_NAME_BASED = 3;
public final static byte TYPE_RANDOM_BASED = 4;
/*
* 'Standard' namespaces defined (suggested) by UUID specs:
*/
public final static String NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
public final static String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
public final static String NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
public final static String NAMESPACE_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8";
/*
* By default let's cache desc, can be turned off. For hash code there's no
* point in turning it off (since the int is already part of the instance
* memory allocation); if you want to save those 4 bytes (or possibly bit
* more if alignment is bad) just comment out hash caching.
*/
private static boolean sDescCaching = true;
/**
* The shared null UUID. Would be nice to do lazy instantiation, but if the
* instance really has to be a singleton, that would mean class-level
* locking (synchronized getNullUUID()), which would be some overhead... So
* let's just bite the bullet the first time assuming creation of the null
* UUID (plus wasted space if it's not needed) can be ignored.
*/
private final static UUID sNullUUID = new UUID();
private final byte[] mId = new byte[16];
// Both string presentation and hash value may be cached...
private transient String mDesc = null;
private transient int mHashCode = 0;
/* *** Object creation: *** */
/**
* Default constructor creates a NIL UUID, one that contains all zeroes
*
* Note that the clearing of array is actually unnecessary as JVMs are
* required to clear up the allocated arrays by default.
*/
public UUID() {
/*
* for (int i = 0; i < 16; ++i) { mId[i] = (byte)0; }
*/
}
/**
* Constructor for cases where you already have the 16-byte binary
* representation of the UUID (for example if you save UUIDs binary takes
* less than half of space string representation takes).
*
* @param data
* array that contains the binary representation of UUID
*/
public UUID(byte[] data) {
/*
* Could call the other constructor... and/or use System.arraycopy.
* However, it's likely that those would make this slower to use, and
* initialization is really simple as is in any case.
*/
for (int i = 0; i < 16; ++i) {
mId[i] = data[i];
}
}
/**
* Constructor for cases where you already have the binary representation of
* the UUID (for example if you save UUIDs binary takes less than half of
* space string representation takes) in a byte array
*
* @param data
* array that contains the binary representation of UUID
* @param start
* byte offset where UUID starts
*/
public UUID(byte[] data, int start) {
for (int i = 0; i < 16; ++i) {
mId[i] = data[start + i];
}
}
/**
* Protected constructor used by UUIDGenerator
*
* @param type
* UUID type
* @param data
* 16 byte UUID contents
*/
UUID(int type, byte[] data) {
for (int i = 0; i < 16; ++i) {
mId[i] = data[i];
}
// Type is multiplexed with time_hi:
mId[INDEX_TYPE] &= (byte) 0x0F;
mId[INDEX_TYPE] |= (byte) (type << 4);
// Variant masks first two bits of the clock_seq_hi:
mId[INDEX_VARIATION] &= (byte) 0x3F;
mId[INDEX_VARIATION] |= (byte) 0x80;
}
/**
* Constructor for creating UUIDs from the canonical string representation
*
* Note that implementation is optimized for speed, not necessarily code
* clarity... Also, since what we get might not be 100% canonical (see
* below), let's not yet populate mDesc here.
*
* @param id
* String that contains the canonical representation of the UUID
* to build; 36-char string (see UUID specs for details).
* Hex-chars may be in upper-case too; UUID class will always
* output them in lowercase.
*/
public UUID(String id) throws NumberFormatException {
if (id == null) {
throw new NullPointerException();
}
if (id.length() != 36) {
throw new NumberFormatException(
"UUID has to be represented by the standard 36-char representation");
}
for (int i = 0, j = 0; i < 36; ++j) {
// Need to bypass hyphens:
switch (i) {
case 8:
case 13:
case 18:
case 23:
if (id.charAt(i) != '-') {
throw new NumberFormatException(
"UUID has to be represented by the standard 36-char representation");
}
++i;
}
char c = id.charAt(i);
if (c >= '0' && c <= '9') {
mId[j] = (byte) ((c - '0') << 4);
} else if (c >= 'a' && c <= 'f') {
mId[j] = (byte) ((c - 'a' + 10) << 4);
} else if (c >= 'A' && c <= 'F') {
mId[j] = (byte) ((c - 'A' + 10) << 4);
} else {
throw new NumberFormatException("Non-hex character '" + c + "'");
}
c = id.charAt(++i);
if (c >= '0' && c <= '9') {
mId[j] |= (byte) (c - '0');
} else if (c >= 'a' && c <= 'f') {
mId[j] |= (byte) (c - 'a' + 10);
} else if (c >= 'A' && c <= 'F') {
mId[j] |= (byte) (c - 'A' + 10);
} else {
throw new NumberFormatException("Non-hex character '" + c + "'");
}
++i;
}
}
/**
* Default cloning behaviour (bitwise copy) is just fine...
*
* Could clear out cached string presentation, but there's probably no point
* in doing that.
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// shouldn't happen
return null;
}
}
/* *** Configuration: *** */
public static void setDescCaching(boolean state) {
sDescCaching = state;
}
/* *** Accessors: *** */
/**
* Accessor for getting the shared null UUID
*
* @return the shared null UUID
*/
public static UUID getNullUUID() {
return sNullUUID;
}
public boolean isNullUUID() {
// Assuming null uuid is usually used for nulls:
if (this == sNullUUID) {
return true;
}
// Could also check hash code; null uuid has -1 as hash?
byte[] data = mId;
int i = mId.length;
byte zero = (byte) 0;
while (--i >= 0) {
if (data[i] != zero) {
return false;
}
}
return true;
}
/**
* Returns the UUID type code
*
* @return UUID type
*/
public int getType() {
return (mId[INDEX_TYPE] & 0xFF) >> 4;
}
/**
* Returns the UUID as a 16-byte byte array
*
* @return 16-byte byte array that contains UUID bytes in the network byte
* order
*/
public byte[] asByteArray() {
byte[] result = new byte[16];
toByteArray(result);
return result;
}
/**
* Fills in the 16 bytes (from index pos) of the specified byte array with
* the UUID contents.
*
* @param dst
* Byte array to fill
* @param pos
* Offset in the array
*/
public void toByteArray(byte[] dst, int pos) {
byte[] src = mId;
for (int i = 0; i < 16; ++i) {
dst[pos + i] = src[i];
}
}
public void toByteArray(byte[] dst) {
toByteArray(dst, 0);
}
/**
* 'Synonym' for 'asByteArray'
*/
public byte[] toByteArray() {
return asByteArray();
}
/* *** Standard methods from Object overridden: *** */
/**
* Could use just the default hash code, but we can probably create a better
* identity hash (ie. same contents generate same hash) manually, without
* sacrificing speed too much. Although multiplications with modulos would
* generate better hashing, let's use just shifts, and do 2 bytes at a time.
*
* Of course, assuming UUIDs are randomized enough, even simpler approach
* might be good enough?
*
* Is this a good hash? ... one of these days I better read more about basic
* hashing techniques I swear!
*/
private final static int[] kShifts = { 3, 7, 17, 21, 29, 4, 9 };
public int hashCode() {
if (mHashCode == 0) {
// Let's handle first and last byte separately:
int result = mId[0] & 0xFF;
result |= (result << 16);
result |= (result << 8);
for (int i = 1; i < 15; i += 2) {
int curr = (mId[i] & 0xFF) << 8 | (mId[i + 1] & 0xFF);
int shift = kShifts[i >> 1];
if (shift > 16) {
result ^= (curr << shift) | (curr >>> (32 - shift));
} else {
result ^= (curr << shift);
}
}
// and then the last byte:
int last = mId[15] & 0xFF;
result ^= (last << 3);
result ^= (last << 13);
result ^= (last << 27);
// Let's not accept hash 0 as it indicates 'not hashed yet':
if (result == 0) {
mHashCode = -1;
} else {
mHashCode = result;
}
}
return mHashCode;
}
public String toString() {
/*
* Could be synchronized, but there isn't much harm in just taking our
* chances (ie. in the worst case we'll form the string more than
* once... but result is the same)
*/
if (mDesc == null) {
StringBuffer b = new StringBuffer(36);
for (int i = 0; i < 16; ++i) {
// Need to bypass hyphens:
switch (i) {
case 4:
case 6:
case 8:
case 10:
b.append('-');
}
int hex = mId[i] & 0xFF;
b.append(kHexChars.charAt(hex >> 4));
b.append(kHexChars.charAt(hex & 0x0f));
}
if (!sDescCaching) {
return b.toString();
}
mDesc = b.toString();
}
return mDesc;
}
/* *** Comparison methods: *** */
private final static int[] sTimeCompare = new int[] { INDEX_CLOCK_HI, INDEX_CLOCK_HI + 1,
INDEX_CLOCK_MID, INDEX_CLOCK_MID + 1, INDEX_CLOCK_LO, INDEX_CLOCK_LO + 1, INDEX_CLOCK_LO + 2,
INDEX_CLOCK_LO + 3, };
/**
* Let's also make UUIDs sortable. This will mostly/only be useful with
* time-based UUIDs; they will sorted by time of creation. The order will be
* strictly correct with UUIDs produced over one JVM's lifetime; that is, if
* more than one JVMs create UUIDs and/or system is rebooted the order may
* not be 100% accurate between UUIDs created under different JVMs.
*
* For all UUIDs, type is first compared, and UUIDs of different types are
* sorted together (ie. null UUID is before all other UUIDs, then time-based
* UUIDs etc). If types are the same, time-based UUIDs' time stamps
* (including additional clock counter) are compared, so UUIDs created first
* are ordered first. For all other types (and for time-based UUIDs with
* same time stamp, which should only occur when comparing a UUID with
* itself, or with UUIDs created on different JVMs or external systems)
* binary comparison is done over all 16 bytes.
*
* @param o
* Object to compare this UUID to; should be a UUID
*
* @return -1 if this UUID should be ordered before the one passed, 1 if
* after, and 0 if they are the same
*
* @throws ClassCastException
* if o is not a UUID.
*/
public int compareTo(Object o) {
UUID other = (UUID) o;
int thisType = getType();
int thatType = other.getType();
/*
* Let's first order by type:
*/
if (thisType > thatType) {
return 1;
} else if (thisType < thatType) {
return -1;
}
/*
* And for time-based UUIDs let's compare time stamps first, then the
* rest... For all other types, we'll just do straight byte-by-byte
* comparison.
*/
byte[] thisId = mId;
byte[] thatId = other.mId;
int i = 0;
if (thisType == TYPE_TIME_BASED) {
for (; i < 8; ++i) {
int index = sTimeCompare[i];
int cmp = (((int) thisId[index]) & 0xFF) - (((int) thatId[index]) & 0xFF);
if (cmp != 0) {
return cmp;
}
}
// Let's fall down to full comparison otherwise
}
for (; i < 16; ++i) {
int cmp = (((int) thisId[i]) & 0xFF) - (((int) thatId[i]) & 0xFF);
if (cmp != 0) {
return cmp;
}
}
return 0;
}
/**
* Checking equality of UUIDs is easy; just compare the 128-bit number.
*/
public boolean equals(Object o) {
if (!(o instanceof UUID)) {
return false;
}
byte[] otherId = ((UUID) o).mId;
byte[] thisId = mId;
for (int i = 0; i < 16; ++i) {
if (otherId[i] != thisId[i]) {
return false;
}
}
return true;
}
/**
* Constructs a new UUID instance given the canonical string representation
* of an UUID.
*
* Note that calling this method returns the same result as would using the
* matching (1 string arg) constructor.
*
* @param id
* Canonical string representation used for constructing an UUID
* instance
*
* @throws NumberFormatException
* if 'id' is invalid UUID
*/
public static UUID valueOf(String id) throws NumberFormatException {
return new UUID(id);
}
/**
* Constructs a new UUID instance given a byte array that contains the (16
* byte) binary representation.
*
* Note that calling this method returns the same result as would using the
* matching constructor
*
* @param src
* Byte array that contains the UUID definition
* @param start
* Offset in the array where the UUID starts
*/
public static UUID valueOf(byte[] src, int start) {
return new UUID(src, start);
}
/**
* Constructs a new UUID instance given a byte array that contains the (16
* byte) binary representation.
*
* Note that calling this method returns the same result as would using the
* matching constructor
*
* @param src
* Byte array that contains the UUID definition
*/
public static UUID valueOf(byte[] src) {
return new UUID(src);
}
public static void main(String[] args) {
}
}