Coverage Report - org.apache.commons.id.uuid.UUID
 
Classes in this File Line Coverage Branch Coverage Complexity
UUID
89%
125/141
67%
36/54
2.5
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.id.uuid;
 18  
 
 19  
 import org.apache.commons.id.DecoderException;
 20  
 import org.apache.commons.id.DigestUtils;
 21  
 import org.apache.commons.id.Hex;
 22  
 
 23  
 import java.io.DataInput;
 24  
 import java.io.IOException;
 25  
 import java.io.Serializable;
 26  
 import java.util.StringTokenizer;
 27  
 
 28  
 
 29  
 /**
 30  
  * <p><code>UUID</code> represents a Universally Unique Identifier per RFC 4122.
 31  
  * See the <a href="ftp://ftp.rfc-editor.org/in-notes/rfc4122.txt">RFC 4122:
 32  
  * A Universally Unique IDentifier (UUID) URN Namespace</a>
 33  
  * for more information.</p>
 34  
  *
 35  
  * @author Commons-Id Team
 36  
  * @version $Revision: 480488 $ $Date: 2006-11-29 08:57:26 +0000 (Wed, 29 Nov 2006) $
 37  
  *
 38  
  */
 39  
 
 40  
 public class UUID implements Constants, Serializable, Comparable {
 41  
         
 42  
         /** byte array to store 128-bits composing this UUID */
 43  107
         private byte[] rawBytes = new byte[UUID_BYTE_LENGTH];
 44  
         
 45  
         /** Holds node identifier for this UUID */
 46  107
         private Long node = null;
 47  
         
 48  
         /** Holds timestamp for this UUID */
 49  107
         private long timestamp = -1;
 50  
         
 51  
         /** Holds the clock sequence field */
 52  107
         private Short clockSq = null;
 53  
         
 54  
         /** Holds the version field of this UUID */
 55  107
         private int version = -1;
 56  
         
 57  
         /** Holds the variant field of this UUID */
 58  107
         private int variant = -1;
 59  
         
 60  
         /** Holds the internal string value of the UUID */
 61  107
         private String stringValue = null;
 62  
         
 63  
         /** Constructs a nil UUID */
 64  
         public UUID() {
 65  3
                 super();
 66  3
         }
 67  
         
 68  
         /**
 69  
          * <p>Constructs a UUID from a 128 bit java.math.BigInteger.</p>
 70  
          * <p>Method is protected as their is no standard as to the internal representation of a UUID.
 71  
          * In this case a BigInteger is used with signum always positive.</p>
 72  
          *
 73  
          *  @param bigIntValue the 128 bit BigInteger to construct this UUID from.
 74  
          *  @throws IllegalArgumentException argument must be 128 bit
 75  
          */
 76  
         /* protected UUID(BigInteger bigIntValue) throws IllegalArgumentException {
 77  
          super();
 78  
          if (bigIntValue.bitLength() > UUID.UUID_BIT_LENGTH) {
 79  
          throw new IllegalArgumentException("UUID must be contructed using a 128 bit BigInteger");
 80  
          }
 81  
          numberValue = bigIntValue;
 82  
          } */
 83  
         
 84  
         /**
 85  
          * <p>Copy constructor.</p>
 86  
          *
 87  
          * @param copyFrom the UUID to copy to create this UUID.
 88  
          */
 89  
         public UUID(UUID copyFrom) {
 90  34
                 super();
 91  34
                 rawBytes = copyFrom.getRawBytes();
 92  34
         }
 93  
         
 94  
         /**
 95  
          * <p>Constructs a UUID from a 16 byte array.</p>
 96  
          *
 97  
          *  @param byteArray the 16 byte array to construct this UUID from.
 98  
          *  @throws IllegalArgumentException argument must be 16 bytes
 99  
          */
 100  
         public UUID(byte[] byteArray) throws IllegalArgumentException {
 101  67
                 super();
 102  67
                 if (byteArray.length != UUID_BYTE_LENGTH) {
 103  0
                         throw new IllegalArgumentException("UUID must be contructed using a 16 byte array.");
 104  
                 }
 105  
                 // UUID must be immutable so a copy is used.
 106  67
                 System.arraycopy(byteArray, 0, rawBytes, 0, UUID_BYTE_LENGTH);
 107  67
         }
 108  
         
 109  
         /**
 110  
          * <p>Constructs a UUID from a DataInput. Note if 16 bytes are not available this method will block.</p>
 111  
          *
 112  
          *  @param input the datainput with 16 bytes to read in from.
 113  
          *  @throws IOException exception if there is an IO problem also argument must contain 16 bytes.
 114  
          */
 115  
         public UUID(DataInput input) throws IOException {
 116  2
                 super();
 117  2
                 input.readFully(rawBytes, 0, UUID_BYTE_LENGTH);
 118  1
         }
 119  
         
 120  
         /**
 121  
          * <p>Constructs a UUID from two long values in most significant byte, and least significant bytes order.</p>
 122  
          *
 123  
          * @param mostSignificant - the most significant 8 bytes of the uuid to be constructed.
 124  
          * @param leastSignificant - the least significant 8 bytes of the uuid to be constructed.
 125  
          */
 126  1
         public UUID(long mostSignificant, long leastSignificant) {
 127  1
                 rawBytes = Bytes.append(Bytes.toBytes(mostSignificant), Bytes.toBytes(leastSignificant));
 128  1
         }
 129  
         
 130  
         /**
 131  
          * <p>Constructs a UUID from a UUID formatted String.</p>
 132  
          *
 133  
          *  @param uuidString the String representing a UUID to construct this UUID
 134  
          *  @throws UUIDFormatException String must be a properly formatted UUID string
 135  
          */
 136  
         public UUID(String uuidString) throws UUIDFormatException {
 137  
                 //Calls the copy constructor
 138  37
                 this(UUID.fromString(uuidString));
 139  33
         }
 140  
         
 141  
         /**
 142  
          *  <p>Parses a string for a UUID.</p>
 143  
          *
 144  
          *  @param uuidString the UUID formatted String to parse.
 145  
          *  @throws UUIDFormatException the String must be a properly formatted UUID String.
 146  
          *  @return Returns a UUID or null if the formatted string could not be parsed.
 147  
          */
 148  
         public static UUID fromString(String uuidString)
 149  
         throws UUIDFormatException {
 150  48
                 String leanString = uuidString.toLowerCase();
 151  48
                 UUID tmpUUID = null;
 152  
                 
 153  
                 //Handle prefixed UUIDs
 154  
                 // e.g. urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6
 155  48
                 int pos = uuidString.lastIndexOf(":");
 156  48
                 if (pos > 1) {
 157  6
                         leanString = uuidString.substring(++pos, uuidString.length());
 158  
                 }
 159  
                 
 160  
                 //Check for 36 char length
 161  48
                 if (leanString.length() != UUID_FORMATTED_LENGTH) {
 162  4
                         throw new UUIDFormatException(uuidString);
 163  
                 }
 164  
                 
 165  
                 //Check for 5 fields
 166  44
                 StringTokenizer tok = new StringTokenizer(leanString, "-");
 167  44
                 if ( tok.countTokens() != TOKENS_IN_UUID ) {
 168  0
                         throw new UUIDFormatException(uuidString);
 169  
                 }
 170  
                 
 171  
                 //Remove the "-" from the formatted string and test token sizes
 172  44
                 StringBuffer buf = new StringBuffer(UUID_UNFORMATTED_LENGTH);
 173  44
                 String token = null;
 174  44
                 int count = 0;
 175  260
                 while (tok.hasMoreTokens()) {
 176  218
                         token = tok.nextToken();
 177  218
                         if (token.length() != TOKEN_LENGTHS[count++]) {
 178  2
                                 throw new UUIDFormatException(uuidString);
 179  
                         }
 180  216
                         buf.append(token);
 181  216
                 }
 182  
                 
 183  
                 //Create from the hex value
 184  
                 try {
 185  42
                         char[] chars = buf.toString().toCharArray();
 186  42
                         tmpUUID = new UUID(Hex.decodeHex(chars));
 187  2
                 } catch (DecoderException de) {
 188  2
                         throw new UUIDFormatException(uuidString + ": " + de.getMessage());
 189  40
                 }
 190  40
                 return tmpUUID;
 191  
         }
 192  
         
 193  
         /**
 194  
          * <p>Returns a string representation of the UUID.</p>
 195  
          *
 196  
          * @return a string representation of the UUID formatted according to the specification.
 197  
          */
 198  
         public String toString() {
 199  
                 //set string value if not set
 200  19
                 if (stringValue == null) {
 201  19
                         StringBuffer buf = new StringBuffer(new String(Hex.encodeHex(rawBytes)));
 202  19
                         while (buf.length() != UUID_UNFORMATTED_LENGTH) {
 203  0
                                 buf.insert(0, "0");
 204  0
                         }
 205  19
                         buf.ensureCapacity(UUID_FORMATTED_LENGTH);
 206  19
                         buf.insert(FORMAT_POSITION1, '-');
 207  19
                         buf.insert(FORMAT_POSITION2, '-');
 208  19
                         buf.insert(FORMAT_POSITION3, '-');
 209  19
                         buf.insert(FORMAT_POSITION4, '-');
 210  19
                         stringValue = buf.toString();
 211  
                 }
 212  19
                 return stringValue;
 213  
         }
 214  
         
 215  
         /**
 216  
          * <p>Returns a urn representation of the UUID. This is same as the
 217  
          * toString() value prefixed with <code>urn:uuid:</code></p>
 218  
          *
 219  
          * @return Returns the urn string representation of the UUID
 220  
          */
 221  
         public String toUrn() {
 222  3
                 return URN_PREFIX + this.toString();
 223  
         }
 224  
         
 225  
         /**
 226  
          * <p>Compares two UUID for equality.</p>
 227  
          *
 228  
          * @see java.lang.Object#equals(Object)
 229  
          */
 230  
         
 231  
         public boolean equals(Object obj) {
 232  19
                 if (!(obj instanceof UUID)) {
 233  0
                         return false;
 234  
                 }
 235  19
                 return Bytes.areEqual( ((UUID) obj).getRawBytes(), rawBytes);
 236  
         }
 237  
         
 238  
         /**
 239  
          * <p>Returns a hash code value for the object.</p>
 240  
          *
 241  
          * @see java.lang.Object#hashCode()
 242  
          */
 243  
         public int hashCode() {
 244  14
                 int iConstant = 37;
 245  14
                 int iTotal = 17;
 246  238
                 for (int i = 0; i < rawBytes.length; i++) {
 247  224
                         iTotal = iTotal * iConstant + rawBytes[i];
 248  
                 }
 249  14
                 return iTotal;
 250  
         }
 251  
         
 252  
         /**
 253  
          * <p>Compares two UUID's for equality.</p>
 254  
          *
 255  
          * @see Comparable#compareTo(Object)
 256  
          */
 257  
         public int compareTo(Object compareTo) throws ClassCastException {
 258  3
                 if (!(compareTo instanceof UUID)) {
 259  0
                         throw new ClassCastException();
 260  
                 }
 261  3
                 return (Bytes.compareTo(rawBytes, ((UUID) compareTo).getRawBytes()));
 262  
         }
 263  
         
 264  
         /**
 265  
          * <p>Returns the clock sequence value in the UUID. The clock sequence is a random assigned to a particular clock instance that
 266  
          * generated the time in the timestamp of a time based UUID.</p>
 267  
          *
 268  
          * @return the clock sequence value in the UUID.
 269  
          * @throws UnsupportedOperationException thrown if this is not a IETF variant or not a time-based UUID.
 270  
          */
 271  
         public int clockSequence() throws UnsupportedOperationException {
 272  
                 //if variant is not mealling leach salz throw unsupported operation exception
 273  1
                 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
 274  0
                         throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
 275  
                 }
 276  1
                 if (clockSq == null) {
 277  1
                         byte[] b = {((byte) (rawBytes[8] & 0x3F)), rawBytes[9]};
 278  1
                         clockSq = new Short(Bytes.toShort(b));
 279  
                 }
 280  1
                 return clockSq.intValue();
 281  
         }
 282  
         
 283  
         /**
 284  
          * <p>Returns the version of the UUID.
 285  
          * <ul>
 286  
          *   <li>VERSION_ONE - The time-based version</li>
 287  
          *   <li>VERSION_TWO - DCE Security version, with embedded POSIX UIDs.</li>
 288  
          *   <li>VERSION_THREE - Name based UUID with MD5 hashing.</li>
 289  
          *   <li>VERSION_FOUR - Random based UUID.</li>
 290  
          *   <li>VERSION_FIVE - Name based UUID with SHA-1 hashing.</li>
 291  
          * </ul>
 292  
          * </p>
 293  
          * @return the version of the UUID.
 294  
          */
 295  
         public int version() {
 296  14
                 if (version == -1) {
 297  14
                         version = ((rawBytes[6] >>> 4) & 0x0F);
 298  
                 }
 299  14
                 return version;
 300  
         }
 301  
         
 302  
         /**
 303  
          * <p>Returns the variant field of the UUID.</p>
 304  
          *
 305  
          * @return Returns the variant field of the UUID.
 306  
          * @see UUID#VARIANT_NCS_COMPAT
 307  
          * @see UUID#VARIANT_IETF_DRAFT
 308  
          * @see UUID#VARIANT_MS
 309  
          * @see UUID#VARIANT_FUTURE
 310  
          */
 311  
         public int variant() {
 312  9
                 if (variant == -1) {
 313  9
                         if ((rawBytes[8] & 0x80) == 0x0) {
 314  1
                                 variant = VARIANT_NCS_COMPAT;
 315  1
                         } else if ((rawBytes[8] & 0x40) == 0x0) {
 316  6
                                 variant = VARIANT_IETF_DRAFT;
 317  6
                         } else if ((rawBytes[8] & 0x20) == 0x0) {
 318  1
                                 variant = VARIANT_MS;
 319  1
                         } else {
 320  1
                                 variant = VARIANT_FUTURE;
 321  
                         }
 322  
                 }
 323  9
                 return variant;
 324  
         }
 325  
         
 326  
         /**
 327  
          * <p>Returns the node identifier found in this UUID. The specification was written such that this value holds the IEEE 802 MAC
 328  
          * address. The specification permits this value to be calculated from other sources other than the MAC.</p>
 329  
          *
 330  
          * @return the node identifier found in this UUID.
 331  
          * @throws UnsupportedOperationException thrown if this is not a IETF variant or not a time-based UUID.
 332  
          */
 333  
         public long node() throws UnsupportedOperationException {
 334  
                 //if variant is not mealling leach salz throw unsupported operation exception
 335  1
                 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
 336  0
                         throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
 337  
                 }
 338  1
                 if (node == null) {
 339  1
                         byte[] b = new byte[8];
 340  1
                         System.arraycopy(rawBytes, 10, b, 2, 6);
 341  1
                         node = new Long((Bytes.toLong(b) & 0xFFFFFFFFFFFFL));
 342  
                 }
 343  1
                 return node.longValue();
 344  
         }
 345  
         
 346  
         /**
 347  
          * <p>Returns the timestamp value of the UUID as 100-nano second intervals since the Gregorian change offset (00:00:00.00, 15
 348  
          * October 1582 ).</p>
 349  
          *
 350  
          * @return the timestamp value of the UUID as 100-nano second intervals since the Gregorian change offset.
 351  
          * @throws UnsupportedOperationException thrown if this is not a IETF variant or not a time-based UUID.
 352  
          */
 353  
         public long timestamp() throws UnsupportedOperationException {
 354  
                 //if variant is not mealling leach salz throw unsupported operation exception
 355  1
                 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
 356  0
                         throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
 357  
                 }
 358  1
                 if (timestamp == -1) {
 359  1
                         byte[] longVal = new byte[8];
 360  1
                         System.arraycopy(rawBytes, TIME_HI_START_POS, longVal, TIME_HI_TS_POS, TIME_HI_BYTE_LEN);
 361  1
                         System.arraycopy(rawBytes, TIME_MID_START_POS, longVal, TIME_MID_TS_POS, TIME_MID_BYTE_LEN);
 362  1
                         System.arraycopy(rawBytes, TIME_LOW_START_POS, longVal, TIME_LOW_TS_POS, TIME_LOW_BYTE_LEN);
 363  1
                         longVal[TIME_HI_TS_POS] &= 0x0F;
 364  1
                         timestamp = Bytes.toLong(longVal);
 365  
                 }
 366  1
                 return timestamp;
 367  
         }
 368  
         
 369  
         /**
 370  
          * <p>Returns the least significant bits stored in the uuid's internal structure.</p>
 371  
          *
 372  
          * @return the least significant bits stored in the uuid's internal structure.
 373  
          */
 374  
         long getLeastSignificantBits()  {
 375  0
                 byte[] lsb = new byte[8];
 376  0
                 System.arraycopy(rawBytes, 8, lsb, 0, 8);
 377  0
                 return Bytes.toLong(lsb);
 378  
         }
 379  
         
 380  
         /**
 381  
          * <p>Returns the least significant bits stored in the uuid's internal structure.</p>
 382  
          *
 383  
          * @return the least significant bits stored in the uuid's internal structure.
 384  
          */
 385  
         long getMostSignificantBits()  {
 386  0
                 byte[] msb = new byte[8];
 387  0
                 System.arraycopy(rawBytes, 0, msb, 0, 8);
 388  0
                 return Bytes.toLong(msb);
 389  
         }
 390  
         
 391  
         /**
 392  
          * <p>Returns a copy of the byte values contained in this UUID.
 393  
          *
 394  
          * @return a copy of the byte values contained in this UUID.
 395  
          */
 396  
         public byte[] getRawBytes() {
 397  76
                 byte[] ret = new byte[UUID_BYTE_LENGTH];
 398  76
                 System.arraycopy(rawBytes, 0, ret, 0, UUID_BYTE_LENGTH);
 399  76
                 return ret;
 400  
         }
 401  
         
 402  
         /**
 403  
          * <p>Returns a new version 4 UUID, based upon Random bits.</p>
 404  
          *
 405  
          * @return a new version 4 UUID, based upon Random bits.
 406  
          */
 407  
         public static UUID randomUUID() {
 408  1
                 return VersionFourGenerator.getInstance().nextUUID();
 409  
         }
 410  
         
 411  
         /**
 412  
          * <p>Returns a new version 1 UUID, based upon node identifier and time stamp.</p>
 413  
          *
 414  
          * @return a new version 1 UUID, based upon node identifier and time stamp.
 415  
          */
 416  
         public static UUID timeUUID() {
 417  1
                 return VersionOneGenerator.getInstance().nextUUID();
 418  
         }
 419  
         
 420  
         /**
 421  
          * <p>Returns a new version three (MD5) or five (SHA-1) UUID, using the specified encoding
 422  
          *  given a name and the namespace's UUID.</p>
 423  
          *
 424  
          * @param name String the name to calculate the UUID for.
 425  
          * @param namespace UUID assigned to this namespace.
 426  
          * @param encoding The encoding to use, either #{link UUID.MD5_ENCODING} or #{link UUID.SHA1_ENCODING}
 427  
          * @return a new version three UUID given a name and the namespace's UUID.
 428  
          */
 429  
         public static UUID nameUUIDFromString(String name, UUID namespace, String encoding) {
 430  13
                 byte[] nameAsBytes = name.getBytes();
 431  13
                 byte[] concat = new byte[UUID_BYTE_LENGTH + nameAsBytes.length];
 432  13
                 System.arraycopy(namespace.getRawBytes(), 0, concat, 0, UUID_BYTE_LENGTH);
 433  13
                 System.arraycopy(nameAsBytes, 0, concat, UUID_BYTE_LENGTH, nameAsBytes.length);
 434  
                 
 435  13
                 byte[] raw = null;
 436  
                 
 437  13
                 if(encoding.equals(UUID.MD5_ENCODING)) {
 438  7
                         raw = DigestUtils.md5(concat);
 439  7
                 }
 440  6
                 else if(encoding.equals(UUID.SHA1_ENCODING)) {
 441  6
                         byte[] shaDigest = DigestUtils.sha(concat);
 442  
                         // Truncate digest to 16 bytes (SHA-1 returns a 20-byte digest)
 443  6
                         raw = new byte[16];
 444  6
                         System.arraycopy(shaDigest, 0, raw, 0, 16); 
 445  6
                 }
 446  
                 else {
 447  0
                         throw new RuntimeException("Unsupported encoding " + encoding);
 448  
                 }
 449  
                 
 450  
                 
 451  
                 //Set version (version 3 and version 5 are identical on a bit-level,
 452  
                 //thus we only need ever set one of them
 453  13
                 raw[TIME_HI_AND_VERSION_BYTE_6] &= 0x0F;
 454  13
                 raw[TIME_HI_AND_VERSION_BYTE_6] |= (UUID.VERSION_THREE << 4);
 455  
                 
 456  
                 //Set variant
 457  13
                 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] &= 0x3F; //0011 1111
 458  13
                 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] |= 0x80; //1000 0000
 459  
                 
 460  13
                 return new UUID(raw);
 461  
         }
 462  
         
 463  
         /**
 464  
          * <p>Returns a new version three UUID given a name and the namespace's UUID.</p>
 465  
          *
 466  
          * @param name String the name to calculate the UUID for.
 467  
          * @param namespace UUID assigned to this namespace.
 468  
          * @return a new version three UUID given a name and the namespace's UUID.
 469  
          *
 470  
          */
 471  
         public static UUID nameUUIDFromString(String name, UUID namespace) {
 472  5
                 return nameUUIDFromString(name, namespace, UUID.MD5_ENCODING);
 473  
         }
 474  
         
 475  
 }