001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.asn1;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Collection;
027
028 import com.unboundid.util.ByteStringBuffer;
029 import com.unboundid.util.NotMutable;
030 import com.unboundid.util.ThreadSafety;
031 import com.unboundid.util.ThreadSafetyLevel;
032
033 import static com.unboundid.asn1.ASN1Constants.*;
034 import static com.unboundid.asn1.ASN1Messages.*;
035 import static com.unboundid.util.Debug.*;
036
037
038
039 /**
040 * This class provides an ASN.1 set element, which is used to hold a set of
041 * zero or more other elements (potentially including additional "envelope"
042 * element types like other sequences and/or sets) in which the order of those
043 * elements should not be considered significant.
044 */
045 @NotMutable()
046 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
047 public final class ASN1Set
048 extends ASN1Element
049 {
050 /**
051 * The serial version UID for this serializable class.
052 */
053 private static final long serialVersionUID = -523497075310394409L;
054
055
056
057 /*
058 * NOTE: This class uses lazy initialization for the encoded value. The
059 * encoded value should only be needed by the getValue() method, which is used
060 * by ASN1Element.encode(). Even though this class is externally immutable,
061 * that does not by itself make it completely threadsafe, because weirdness in
062 * the Java memory model could allow the assignment to be performed out of
063 * order. By passing the value through a volatile variable any time the value
064 * is set other than in the constructor (which will always be safe) we ensure
065 * that this reordering cannot happen.
066 *
067 * In the majority of cases, passing the value through assignments to
068 * valueBytes through a volatile variable is much faster than declaring
069 * valueBytes itself to be volatile because a volatile variable cannot be held
070 * in CPU caches or registers and must only be accessed from memory visible to
071 * all threads. Since the value may be read much more often than it is
072 * written, passing it through a volatile variable rather than making it
073 * volatile directly can help avoid that penalty when possible.
074 */
075
076
077
078 // The set of ASN.1 elements contained in this set.
079 private final ASN1Element[] elements;
080
081 // The encoded representation of the value, if available.
082 private byte[] encodedValue;
083
084 // A volatile variable used to guard publishing the encodedValue array. See
085 // the note above to explain why this is needed.
086 private volatile byte[] encodedValueGuard;
087
088
089
090 /**
091 * Creates a new ASN.1 set with the default BER type and no encapsulated
092 * elements.
093 */
094 public ASN1Set()
095 {
096 super(UNIVERSAL_SET_TYPE);
097
098 elements = NO_ELEMENTS;
099 encodedValue = NO_VALUE;
100 }
101
102
103
104 /**
105 * Creates a new ASN.1 set with the specified BER type and no encapsulated
106 * elements.
107 *
108 * @param type The BER type to use for this element.
109 */
110 public ASN1Set(final byte type)
111 {
112 super(type);
113
114 elements = NO_ELEMENTS;
115 encodedValue = NO_VALUE;
116 }
117
118
119
120 /**
121 * Creates a new ASN.1 set with the default BER type and the provided set of
122 * elements.
123 *
124 * @param elements The set of elements to include in this set.
125 */
126 public ASN1Set(final ASN1Element... elements)
127 {
128 super(UNIVERSAL_SET_TYPE);
129
130 if (elements == null)
131 {
132 this.elements = NO_ELEMENTS;
133 }
134 else
135 {
136 this.elements = elements;
137 }
138
139 encodedValue = null;
140 }
141
142
143
144 /**
145 * Creates a new ASN.1 set with the default BER type and the provided set of
146 * elements.
147 *
148 * @param elements The set of elements to include in this set.
149 */
150 public ASN1Set(final Collection<? extends ASN1Element> elements)
151 {
152 super(UNIVERSAL_SET_TYPE);
153
154 if ((elements == null) || elements.isEmpty())
155 {
156 this.elements = NO_ELEMENTS;
157 }
158 else
159 {
160 this.elements = new ASN1Element[elements.size()];
161 elements.toArray(this.elements);
162 }
163
164 encodedValue = null;
165 }
166
167
168
169 /**
170 * Creates a new ASN.1 set with the specified BER type and the provided set of
171 * elements.
172 *
173 * @param type The BER type to use for this element.
174 * @param elements The set of elements to include in this set.
175 */
176 public ASN1Set(final byte type, final ASN1Element... elements)
177 {
178 super(type);
179
180 if (elements == null)
181 {
182 this.elements = NO_ELEMENTS;
183 }
184 else
185 {
186 this.elements = elements;
187 }
188
189 encodedValue = null;
190 }
191
192
193
194 /**
195 * Creates a new ASN.1 set with the specified BER type and the provided set of
196 * elements.
197 *
198 * @param type The BER type to use for this element.
199 * @param elements The set of elements to include in this set.
200 */
201 public ASN1Set(final byte type,
202 final Collection<? extends ASN1Element> elements)
203 {
204 super(type);
205
206 if ((elements == null) || elements.isEmpty())
207 {
208 this.elements = NO_ELEMENTS;
209 }
210 else
211 {
212 this.elements = new ASN1Element[elements.size()];
213 elements.toArray(this.elements);
214 }
215
216 encodedValue = null;
217 }
218
219
220
221 /**
222 * Creates a new ASN.1 set with the specified type, set of elements, and
223 * encoded value.
224 *
225 * @param type The BER type to use for this element.
226 * @param elements The set of elements to include in this set.
227 * @param value The pre-encoded value for this element.
228 */
229 private ASN1Set(final byte type, final ASN1Element[] elements,
230 final byte[] value)
231 {
232 super(type);
233
234 this.elements = elements;
235 encodedValue = value;
236 }
237
238
239
240 /**
241 * {@inheritDoc}
242 */
243 @Override()
244 byte[] getValueArray()
245 {
246 return getValue();
247 }
248
249
250
251 /**
252 * {@inheritDoc}
253 */
254 @Override()
255 int getValueOffset()
256 {
257 return 0;
258 }
259
260
261
262 /**
263 * {@inheritDoc}
264 */
265 @Override()
266 public int getValueLength()
267 {
268 return getValue().length;
269 }
270
271
272
273 /**
274 * {@inheritDoc}
275 */
276 @Override()
277 public byte[] getValue()
278 {
279 if (encodedValue == null)
280 {
281 encodedValueGuard = ASN1Sequence.encodeElements(elements);
282 encodedValue = encodedValueGuard;
283 }
284
285 return encodedValue;
286 }
287
288
289
290 /**
291 * {@inheritDoc}
292 */
293 @Override()
294 public void encodeTo(final ByteStringBuffer buffer)
295 {
296 buffer.append(getType());
297
298 if (elements.length == 0)
299 {
300 buffer.append((byte) 0x00);
301 return;
302 }
303
304 // In this case, it will likely be faster to just go ahead and append
305 // encoded representations of all of the elements and insert the length
306 // later once we know it.
307 final int originalLength = buffer.length();
308 for (final ASN1Element e : elements)
309 {
310 e.encodeTo(buffer);
311 }
312
313 buffer.insert(originalLength,
314 encodeLength(buffer.length() - originalLength));
315 }
316
317
318
319 /**
320 * Retrieves the set of encapsulated elements held in this set.
321 *
322 * @return The set of encapsulated elements held in this set.
323 */
324 public ASN1Element[] elements()
325 {
326 return elements;
327 }
328
329
330
331 /**
332 * Decodes the contents of the provided byte array as a set element.
333 *
334 * @param elementBytes The byte array to decode as an ASN.1 set element.
335 *
336 * @return The decoded ASN.1 set element.
337 *
338 * @throws ASN1Exception If the provided array cannot be decoded as a set
339 * element.
340 */
341 public static ASN1Set decodeAsSet(final byte[] elementBytes)
342 throws ASN1Exception
343 {
344 try
345 {
346 int valueStartPos = 2;
347 int length = (elementBytes[1] & 0x7F);
348 if (length != elementBytes[1])
349 {
350 final int numLengthBytes = length;
351
352 length = 0;
353 for (int i=0; i < numLengthBytes; i++)
354 {
355 length <<= 8;
356 length |= (elementBytes[valueStartPos++] & 0xFF);
357 }
358 }
359
360 if ((elementBytes.length - valueStartPos) != length)
361 {
362 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
363 (elementBytes.length - valueStartPos)));
364 }
365
366 final byte[] value = new byte[length];
367 System.arraycopy(elementBytes, valueStartPos, value, 0, length);
368
369 int numElements = 0;
370 final ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>(5);
371 try
372 {
373 int pos = 0;
374 while (pos < value.length)
375 {
376 final byte type = value[pos++];
377
378 final byte firstLengthByte = value[pos++];
379 int l = (firstLengthByte & 0x7F);
380 if (l != firstLengthByte)
381 {
382 final int numLengthBytes = l;
383 l = 0;
384 for (int i=0; i < numLengthBytes; i++)
385 {
386 l <<= 8;
387 l |= (value[pos++] & 0xFF);
388 }
389 }
390
391 final int posPlusLength = pos + l;
392 if ((l < 0) || (posPlusLength < 0) || (posPlusLength > value.length))
393 {
394 throw new ASN1Exception(
395 ERR_SET_BYTES_DECODE_LENGTH_EXCEEDS_AVAILABLE.get());
396 }
397
398 elementList.add(new ASN1Element(type, value, pos, l));
399 pos += l;
400 numElements++;
401 }
402 }
403 catch (final ASN1Exception ae)
404 {
405 throw ae;
406 }
407 catch (final Exception e)
408 {
409 debugException(e);
410 throw new ASN1Exception(ERR_SET_BYTES_DECODE_EXCEPTION.get(e), e);
411 }
412
413 int i = 0;
414 final ASN1Element[] elements = new ASN1Element[numElements];
415 for (final ASN1Element e : elementList)
416 {
417 elements[i++] = e;
418 }
419
420 return new ASN1Set(elementBytes[0], elements, value);
421 }
422 catch (final ASN1Exception ae)
423 {
424 debugException(ae);
425 throw ae;
426 }
427 catch (final Exception e)
428 {
429 debugException(e);
430 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
431 }
432 }
433
434
435
436 /**
437 * Decodes the provided ASN.1 element as a set element.
438 *
439 * @param element The ASN.1 element to be decoded.
440 *
441 * @return The decoded ASN.1 set element.
442 *
443 * @throws ASN1Exception If the provided element cannot be decoded as a set
444 * element.
445 */
446 public static ASN1Set decodeAsSet(final ASN1Element element)
447 throws ASN1Exception
448 {
449 int numElements = 0;
450 final ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>(5);
451 final byte[] value = element.getValue();
452
453 try
454 {
455 int pos = 0;
456 while (pos < value.length)
457 {
458 final byte type = value[pos++];
459
460 final byte firstLengthByte = value[pos++];
461 int length = (firstLengthByte & 0x7F);
462 if (length != firstLengthByte)
463 {
464 final int numLengthBytes = length;
465 length = 0;
466 for (int i=0; i < numLengthBytes; i++)
467 {
468 length <<= 8;
469 length |= (value[pos++] & 0xFF);
470 }
471 }
472
473 final int posPlusLength = pos + length;
474 if ((length < 0) || (posPlusLength < 0) ||
475 (posPlusLength > value.length))
476 {
477 throw new ASN1Exception(
478 ERR_SET_DECODE_LENGTH_EXCEEDS_AVAILABLE.get(
479 String.valueOf(element)));
480 }
481
482 elementList.add(new ASN1Element(type, value, pos, length));
483 pos += length;
484 numElements++;
485 }
486 }
487 catch (final ASN1Exception ae)
488 {
489 throw ae;
490 }
491 catch (final Exception e)
492 {
493 debugException(e);
494 throw new ASN1Exception(
495 ERR_SET_DECODE_EXCEPTION.get(String.valueOf(element), e), e);
496 }
497
498 int i = 0;
499 final ASN1Element[] elements = new ASN1Element[numElements];
500 for (final ASN1Element e : elementList)
501 {
502 elements[i++] = e;
503 }
504
505 return new ASN1Set(element.getType(), elements, value);
506 }
507
508
509
510 /**
511 * Appends a string representation of this ASN.1 element to the provided
512 * buffer.
513 *
514 * @param buffer The buffer to which to append the information.
515 */
516 @Override()
517 public void toString(final StringBuilder buffer)
518 {
519 buffer.append('[');
520 for (int i=0; i < elements.length; i++)
521 {
522 if (i > 0)
523 {
524 buffer.append(',');
525 }
526 elements[i].toString(buffer);
527 }
528 buffer.append(']');
529 }
530 }