/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.handle; import java.io.Serializable; /** * <b>NOTE - code adapted from JUG libary (Copyright (c) 2002- Tatu Saloranta, tatu.saloranta@iki.fi)</b>. See * http://jug.safehaus.org/. * * This code only support type 4 UUID - randomly generated ones. */ public class UUID implements Serializable, Cloneable, Comparable<UUID> { private final static long serialVersionUID = -1; 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"; /** * 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(); 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; } } /* *** 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. *<p> * Of course, assuming UUIDs are randomized enough, even simpler * approach might be good enough? *<p> * 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() { 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)); } return b.toString(); } public int compareTo(UUID o) { UUID other = (UUID) o; byte[] thisId = mId; byte[] thatId = other.mId; for (int i = 0; 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); } }