View Javadoc
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 &lt;major&gt;.&lt;minor&gt;.&lt;bugfix&gt;) 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 }