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