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    }