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