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 private byte[] rawBytes = new byte[UUID_BYTE_LENGTH];
44
45 /** Holds node identifier for this UUID */
46 private Long node = null;
47
48 /** Holds timestamp for this UUID */
49 private long timestamp = -1;
50
51 /** Holds the clock sequence field */
52 private Short clockSq = null;
53
54 /** Holds the version field of this UUID */
55 private int version = -1;
56
57 /** Holds the variant field of this UUID */
58 private int variant = -1;
59
60 /** Holds the internal string value of the UUID */
61 private String stringValue = null;
62
63 /** Constructs a nil UUID */
64 public UUID() {
65 super();
66 }
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 super();
91 rawBytes = copyFrom.getRawBytes();
92 }
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 super();
102 if (byteArray.length != UUID_BYTE_LENGTH) {
103 throw new IllegalArgumentException("UUID must be contructed using a 16 byte array.");
104 }
105 // UUID must be immutable so a copy is used.
106 System.arraycopy(byteArray, 0, rawBytes, 0, UUID_BYTE_LENGTH);
107 }
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 super();
117 input.readFully(rawBytes, 0, UUID_BYTE_LENGTH);
118 }
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 public UUID(long mostSignificant, long leastSignificant) {
127 rawBytes = Bytes.append(Bytes.toBytes(mostSignificant), Bytes.toBytes(leastSignificant));
128 }
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 this(UUID.fromString(uuidString));
139 }
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 String leanString = uuidString.toLowerCase();
151 UUID tmpUUID = null;
152
153 //Handle prefixed UUIDs
154 // e.g. urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6
155 int pos = uuidString.lastIndexOf(":");
156 if (pos > 1) {
157 leanString = uuidString.substring(++pos, uuidString.length());
158 }
159
160 //Check for 36 char length
161 if (leanString.length() != UUID_FORMATTED_LENGTH) {
162 throw new UUIDFormatException(uuidString);
163 }
164
165 //Check for 5 fields
166 StringTokenizer tok = new StringTokenizer(leanString, "-");
167 if ( tok.countTokens() != TOKENS_IN_UUID ) {
168 throw new UUIDFormatException(uuidString);
169 }
170
171 //Remove the "-" from the formatted string and test token sizes
172 StringBuffer buf = new StringBuffer(UUID_UNFORMATTED_LENGTH);
173 String token = null;
174 int count = 0;
175 while (tok.hasMoreTokens()) {
176 token = tok.nextToken();
177 if (token.length() != TOKEN_LENGTHS[count++]) {
178 throw new UUIDFormatException(uuidString);
179 }
180 buf.append(token);
181 }
182
183 //Create from the hex value
184 try {
185 char[] chars = buf.toString().toCharArray();
186 tmpUUID = new UUID(Hex.decodeHex(chars));
187 } catch (DecoderException de) {
188 throw new UUIDFormatException(uuidString + ": " + de.getMessage());
189 }
190 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 if (stringValue == null) {
201 StringBuffer buf = new StringBuffer(new String(Hex.encodeHex(rawBytes)));
202 while (buf.length() != UUID_UNFORMATTED_LENGTH) {
203 buf.insert(0, "0");
204 }
205 buf.ensureCapacity(UUID_FORMATTED_LENGTH);
206 buf.insert(FORMAT_POSITION1, '-');
207 buf.insert(FORMAT_POSITION2, '-');
208 buf.insert(FORMAT_POSITION3, '-');
209 buf.insert(FORMAT_POSITION4, '-');
210 stringValue = buf.toString();
211 }
212 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 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 if (!(obj instanceof UUID)) {
233 return false;
234 }
235 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 int iConstant = 37;
245 int iTotal = 17;
246 for (int i = 0; i < rawBytes.length; i++) {
247 iTotal = iTotal * iConstant + rawBytes[i];
248 }
249 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 if (!(compareTo instanceof UUID)) {
259 throw new ClassCastException();
260 }
261 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 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
274 throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
275 }
276 if (clockSq == null) {
277 byte[] b = {((byte) (rawBytes[8] & 0x3F)), rawBytes[9]};
278 clockSq = new Short(Bytes.toShort(b));
279 }
280 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 if (version == -1) {
297 version = ((rawBytes[6] >>> 4) & 0x0F);
298 }
299 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 if (variant == -1) {
313 if ((rawBytes[8] & 0x80) == 0x0) {
314 variant = VARIANT_NCS_COMPAT;
315 } else if ((rawBytes[8] & 0x40) == 0x0) {
316 variant = VARIANT_IETF_DRAFT;
317 } else if ((rawBytes[8] & 0x20) == 0x0) {
318 variant = VARIANT_MS;
319 } else {
320 variant = VARIANT_FUTURE;
321 }
322 }
323 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 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
336 throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
337 }
338 if (node == null) {
339 byte[] b = new byte[8];
340 System.arraycopy(rawBytes, 10, b, 2, 6);
341 node = new Long((Bytes.toLong(b) & 0xFFFFFFFFFFFFL));
342 }
343 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 if (variant() != VARIANT_IETF_DRAFT || version() != VERSION_ONE) {
356 throw new UnsupportedOperationException(WRONG_VAR_VER_MSG);
357 }
358 if (timestamp == -1) {
359 byte[] longVal = new byte[8];
360 System.arraycopy(rawBytes, TIME_HI_START_POS, longVal, TIME_HI_TS_POS, TIME_HI_BYTE_LEN);
361 System.arraycopy(rawBytes, TIME_MID_START_POS, longVal, TIME_MID_TS_POS, TIME_MID_BYTE_LEN);
362 System.arraycopy(rawBytes, TIME_LOW_START_POS, longVal, TIME_LOW_TS_POS, TIME_LOW_BYTE_LEN);
363 longVal[TIME_HI_TS_POS] &= 0x0F;
364 timestamp = Bytes.toLong(longVal);
365 }
366 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 byte[] lsb = new byte[8];
376 System.arraycopy(rawBytes, 8, lsb, 0, 8);
377 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 byte[] msb = new byte[8];
387 System.arraycopy(rawBytes, 0, msb, 0, 8);
388 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 byte[] ret = new byte[UUID_BYTE_LENGTH];
398 System.arraycopy(rawBytes, 0, ret, 0, UUID_BYTE_LENGTH);
399 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 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 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 byte[] nameAsBytes = name.getBytes();
431 byte[] concat = new byte[UUID_BYTE_LENGTH + nameAsBytes.length];
432 System.arraycopy(namespace.getRawBytes(), 0, concat, 0, UUID_BYTE_LENGTH);
433 System.arraycopy(nameAsBytes, 0, concat, UUID_BYTE_LENGTH, nameAsBytes.length);
434
435 byte[] raw = null;
436
437 if(encoding.equals(UUID.MD5_ENCODING)) {
438 raw = DigestUtils.md5(concat);
439 }
440 else if(encoding.equals(UUID.SHA1_ENCODING)) {
441 byte[] shaDigest = DigestUtils.sha(concat);
442 // Truncate digest to 16 bytes (SHA-1 returns a 20-byte digest)
443 raw = new byte[16];
444 System.arraycopy(shaDigest, 0, raw, 0, 16);
445 }
446 else {
447 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 raw[TIME_HI_AND_VERSION_BYTE_6] &= 0x0F;
454 raw[TIME_HI_AND_VERSION_BYTE_6] |= (UUID.VERSION_THREE << 4);
455
456 //Set variant
457 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] &= 0x3F; //0011 1111
458 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] |= 0x80; //1000 0000
459
460 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 return nameUUIDFromString(name, namespace, UUID.MD5_ENCODING);
473 }
474
475 }