Mega Code Archive

 
Categories / Java / Development Class
 

Your own UUID

/**  * 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.    * <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() {     /*      * 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) {   } }