1 /*
2 * #%L
3 * UuidElement.java - mongodb-async-driver - Allanbank Consulting, Inc.
4 * %%
5 * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6 * %%
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20 package com.allanbank.mongodb.bson.element;
21
22 import static com.allanbank.mongodb.util.Assertions.assertNotNull;
23
24 import java.util.UUID;
25
26 import com.allanbank.mongodb.bson.io.EndianUtils;
27 import com.allanbank.mongodb.util.IOUtils;
28
29 /**
30 * UuidElement provides a helper element for handling UUID {@link BinaryElement}
31 * sub-types.
32 * <p>
33 * If no sub-type is provided this class defaults to the standardized sub-type 4
34 * binary element which encodes the UUID from most significant byte to least
35 * significant byte. If the deprecated sub-type 3 is specified this class
36 * assumes the legacy Java encoding of the UUID which encodes the most
37 * significant long in least-significant-byte order and then the least
38 * significant long in least-significant-byte order.
39 * </p>
40 *
41 * @api.yes This class is part of the driver's API. Public and protected members
42 * will be deprecated for at least 1 non-bugfix release (version
43 * numbers are <major>.<minor>.<bugfix>) before being
44 * removed or modified.
45 * @copyright 2012-2013, Allanbank Consulting, Inc., All Rights Reserved
46 */
47 public class UuidElement extends BinaryElement {
48
49 /**
50 * The legacy (reverse byte order for high and low long values) subtype for
51 * the UUID.
52 */
53 public static final byte LEGACY_UUID_SUBTTYPE = 3;
54
55 /** The length for the UUID binary value. */
56 public static final int UUID_BINARY_LENGTH = 16;
57
58 /** The default subtype for the UUID. */
59 public static final byte UUID_SUBTTYPE = 4;
60
61 /** The serialization version for the class. */
62 private static final long serialVersionUID = 6461067538910973839L;
63
64 /**
65 * Converts the UUID value to a byte array based on the subtype.
66 *
67 * @param uuidSubttype
68 * The subtype for the UUID encoding.
69 * @param value
70 * The UUID value to convert.
71 * @return The byte encoding.
72 */
73 private static byte[] toBytes(final byte uuidSubttype, final UUID value) {
74 assertNotNull(value,
75 "The UUID value for a UuidElement must not be null.");
76
77 long high = value.getMostSignificantBits();
78 long low = value.getLeastSignificantBits();
79
80 if (uuidSubttype == LEGACY_UUID_SUBTTYPE) {
81 high = EndianUtils.swap(high);
82 low = EndianUtils.swap(low);
83 }
84
85 final byte[] result = new byte[16];
86
87 result[0] = (byte) ((high >> 56) & 0xFF);
88 result[1] = (byte) ((high >> 48) & 0xFF);
89 result[2] = (byte) ((high >> 40) & 0xFF);
90 result[3] = (byte) ((high >> 32) & 0xFF);
91 result[4] = (byte) ((high >> 24) & 0xFF);
92 result[5] = (byte) ((high >> 16) & 0xFF);
93 result[6] = (byte) ((high >> 8) & 0xFF);
94 result[7] = (byte) (high & 0xFF);
95 result[8] = (byte) ((low >> 56) & 0xFF);
96 result[9] = (byte) ((low >> 48) & 0xFF);
97 result[10] = (byte) ((low >> 40) & 0xFF);
98 result[11] = (byte) ((low >> 32) & 0xFF);
99 result[12] = (byte) ((low >> 24) & 0xFF);
100 result[13] = (byte) ((low >> 16) & 0xFF);
101 result[14] = (byte) ((low >> 8) & 0xFF);
102 result[15] = (byte) (low & 0xFF);
103
104 return result;
105 }
106
107 /** The UUID value created. */
108 private final UUID myUuid;
109
110 /**
111 * Creates a new UuidElement.
112 *
113 * @param name
114 * The name for the element.
115 * @param subType
116 * The subtype for the UUID element.
117 * @param value
118 * The UUID bytes for the element.
119 * @throws IllegalArgumentException
120 * If the {@code name} or {@code value} is <code>null</code>. If
121 * the subType is not {@link #UUID_SUBTTYPE} or
122 * {@link #LEGACY_UUID_SUBTTYPE}. If the value is not a 16 bytes
123 * long.
124 */
125 public UuidElement(final String name, final byte subType, final byte[] value) {
126 super(name, subType, value);
127
128 myUuid = toUuid(subType, value);
129 }
130
131 /**
132 * Creates a new UuidElement.
133 *
134 * @param name
135 * The name for the element.
136 * @param subType
137 * The subtype for the UUID element.
138 * @param value
139 * The UUID bytes for the element.
140 * @param size
141 * The size of the element when encoded in bytes. If not known
142 * then use the
143 * {@link UuidElement#UuidElement(String, byte, byte[])}
144 * constructor instead.
145 * @throws IllegalArgumentException
146 * If the {@code name} or {@code value} is <code>null</code>. If
147 * the subType is not {@link #UUID_SUBTTYPE} or
148 * {@link #LEGACY_UUID_SUBTTYPE}. If the value is not a 16 bytes
149 * long.
150 */
151 public UuidElement(final String name, final byte subType,
152 final byte[] value, final long size) {
153 super(name, subType, value, size);
154
155 myUuid = toUuid(subType, value);
156 }
157
158 /**
159 * Creates a new UuidElement.
160 *
161 * @param name
162 * The name for the element.
163 * @param subType
164 * The subtype for the UUID element.
165 * @param value
166 * The UUID value for the element.
167 * @throws IllegalArgumentException
168 * If the {@code name} or {@code value} is <code>null</code>.
169 */
170 public UuidElement(final String name, final byte subType, final UUID value) {
171 super(name, subType, toBytes(subType, value));
172
173 myUuid = value;
174 }
175
176 /**
177 * Creates a new UuidElement.
178 *
179 * @param name
180 * The name for the element.
181 * @param value
182 * The UUID value for the element.
183 * @throws IllegalArgumentException
184 * If the {@code name} or {@code value} is <code>null</code>.
185 */
186 public UuidElement(final String name, final UUID value) {
187 super(name, UUID_SUBTTYPE, toBytes(UUID_SUBTTYPE, value));
188
189 myUuid = value;
190 }
191
192 /**
193 * Determines if the passed object is of this same type as this object and
194 * if so that its fields are equal.
195 *
196 * @param object
197 * The object to compare to.
198 *
199 * @see java.lang.Object#equals(java.lang.Object)
200 */
201 @Override
202 public boolean equals(final Object object) {
203 boolean result = false;
204 if (this == object) {
205 result = true;
206 }
207 else if ((object != null) && (getClass() == object.getClass())) {
208 final UuidElement other = (UuidElement) object;
209
210 result = super.equals(object) && myUuid.equals(other.myUuid);
211 }
212 return result;
213 }
214
215 /**
216 * Returns the {@link UUID} value.
217 *
218 * @return The {@link UUID} value.
219 */
220 public UUID getUuid() {
221 return myUuid;
222 }
223
224 /**
225 * {@inheritDoc}
226 * <p>
227 * Returns the UUID value.
228 * </p>
229 * <p>
230 * <b>Note:</b> This value will not be recreated is a Object-->Element
231 * conversion. The sub type is lost in this conversion to an {@link Object}.
232 * </p>
233 */
234 @Override
235 public UUID getValueAsObject() {
236 return myUuid;
237 }
238
239 /**
240 * {@inheritDoc}
241 * <p>
242 * Returns the result of the {@link UUID#toString()}.
243 * </p>
244 */
245 @Override
246 public String getValueAsString() {
247 return myUuid.toString();
248 }
249
250 /**
251 * Computes a reasonable hash code.
252 *
253 * @return The hash code value.
254 */
255 @Override
256 public int hashCode() {
257 int result = 1;
258 result = (31 * result) + super.hashCode();
259 result = (31 * result) + myUuid.hashCode();
260 return result;
261 }
262
263 /**
264 * {@inheritDoc}
265 * <p>
266 * Returns a new {@link BinaryElement}.
267 * </p>
268 */
269 @Override
270 public UuidElement withName(final String name) {
271 if (getName().equals(name)) {
272 return this;
273 }
274 return new UuidElement(name, getSubType(), myUuid);
275 }
276
277 /**
278 * Converts the UUID binary form into a UUID object.
279 *
280 * @param subType
281 * The sub-type for the UUID encoding.
282 * @param value
283 * The encoded UUID.
284 * @return The UUID encoded in the {@code value}.
285 * @throws IllegalArgumentException
286 * If the length of the {@code value} array is not
287 * {@value #UUID_BINARY_LENGTH}.
288 */
289 private UUID toUuid(final byte subType, final byte[] value)
290 throws IllegalArgumentException {
291
292 if (value.length == UUID_BINARY_LENGTH) {
293 long high = 0;
294 long low = 0;
295
296 for (int i = 0; i < 8; ++i) {
297 high <<= Byte.SIZE;
298 high += (value[i] & 0xFF);
299 }
300 for (int i = 8; i < 16; ++i) {
301 low <<= Byte.SIZE;
302 low += (value[i] & 0xFF);
303 }
304
305 if (subType == LEGACY_UUID_SUBTTYPE) {
306 high = EndianUtils.swap(high);
307 low = EndianUtils.swap(low);
308 }
309 return new UUID(high, low);
310 }
311
312 throw new IllegalArgumentException(
313 "The value for a UUID must be 16 bytes long: "
314 + IOUtils.toHex(value));
315 }
316 }