001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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 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)
046    public 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    }