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