1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
31
32
33
34
35
36
37
38
39
40 public class UUID implements Constants, Serializable, Comparable {
41
42
43 private byte[] rawBytes = new byte[UUID_BYTE_LENGTH];
44
45
46 private Long node = null;
47
48
49 private long timestamp = -1;
50
51
52 private Short clockSq = null;
53
54
55 private int version = -1;
56
57
58 private int variant = -1;
59
60
61 private String stringValue = null;
62
63
64 public UUID() {
65 super();
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public UUID(UUID copyFrom) {
90 super();
91 rawBytes = copyFrom.getRawBytes();
92 }
93
94
95
96
97
98
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
106 System.arraycopy(byteArray, 0, rawBytes, 0, UUID_BYTE_LENGTH);
107 }
108
109
110
111
112
113
114
115 public UUID(DataInput input) throws IOException {
116 super();
117 input.readFully(rawBytes, 0, UUID_BYTE_LENGTH);
118 }
119
120
121
122
123
124
125
126 public UUID(long mostSignificant, long leastSignificant) {
127 rawBytes = Bytes.append(Bytes.toBytes(mostSignificant), Bytes.toBytes(leastSignificant));
128 }
129
130
131
132
133
134
135
136 public UUID(String uuidString) throws UUIDFormatException {
137
138 this(UUID.fromString(uuidString));
139 }
140
141
142
143
144
145
146
147
148 public static UUID fromString(String uuidString)
149 throws UUIDFormatException {
150 String leanString = uuidString.toLowerCase();
151 UUID tmpUUID = null;
152
153
154
155 int pos = uuidString.lastIndexOf(":");
156 if (pos > 1) {
157 leanString = uuidString.substring(++pos, uuidString.length());
158 }
159
160
161 if (leanString.length() != UUID_FORMATTED_LENGTH) {
162 throw new UUIDFormatException(uuidString);
163 }
164
165
166 StringTokenizer tok = new StringTokenizer(leanString, "-");
167 if ( tok.countTokens() != TOKENS_IN_UUID ) {
168 throw new UUIDFormatException(uuidString);
169 }
170
171
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
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
195
196
197
198 public String toString() {
199
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
217
218
219
220
221 public String toUrn() {
222 return URN_PREFIX + this.toString();
223 }
224
225
226
227
228
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
240
241
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
254
255
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
266
267
268
269
270
271 public int clockSequence() throws UnsupportedOperationException {
272
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
285
286
287
288
289
290
291
292
293
294
295 public int version() {
296 if (version == -1) {
297 version = ((rawBytes[6] >>> 4) & 0x0F);
298 }
299 return version;
300 }
301
302
303
304
305
306
307
308
309
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
328
329
330
331
332
333 public long node() throws UnsupportedOperationException {
334
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
348
349
350
351
352
353 public long timestamp() throws UnsupportedOperationException {
354
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
371
372
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
382
383
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
393
394
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
404
405
406
407 public static UUID randomUUID() {
408 return VersionFourGenerator.getInstance().nextUUID();
409 }
410
411
412
413
414
415
416 public static UUID timeUUID() {
417 return VersionOneGenerator.getInstance().nextUUID();
418 }
419
420
421
422
423
424
425
426
427
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
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
452
453 raw[TIME_HI_AND_VERSION_BYTE_6] &= 0x0F;
454 raw[TIME_HI_AND_VERSION_BYTE_6] |= (UUID.VERSION_THREE << 4);
455
456
457 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] &= 0x3F;
458 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] |= 0x80;
459
460 return new UUID(raw);
461 }
462
463
464
465
466
467
468
469
470
471 public static UUID nameUUIDFromString(String name, UUID namespace) {
472 return nameUUIDFromString(name, namespace, UUID.MD5_ENCODING);
473 }
474
475 }