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 com.unboundid.util.NotMutable;
026    import com.unboundid.util.ThreadSafety;
027    import com.unboundid.util.ThreadSafetyLevel;
028    
029    import static com.unboundid.asn1.ASN1Constants.*;
030    import static com.unboundid.asn1.ASN1Messages.*;
031    import static com.unboundid.util.Debug.*;
032    
033    
034    
035    /**
036     * This class provides an ASN.1 enumerated element.  Enumerated elements are
037     * very similar to integer elements, and the only real difference between them
038     * is that the individual values of an enumerated element have a symbolic
039     * significance (i.e., each value is associated with a particular meaning),
040     * although this does not impact its encoding other than through the use of a
041     * different default BER type.
042     */
043    @NotMutable()
044    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
045    public final class ASN1Enumerated
046           extends ASN1Element
047    {
048      /**
049       * The serial version UID for this serializable class.
050       */
051      private static final long serialVersionUID = -5915912036130847725L;
052    
053    
054    
055      // The int value for this element.
056      private final int intValue;
057    
058    
059    
060      /**
061       * Creates a new ASN.1 enumerated element with the default BER type and the
062       * provided int value.
063       *
064       * @param  intValue  The int value to use for this element.
065       */
066      public ASN1Enumerated(final int intValue)
067      {
068        super(UNIVERSAL_ENUMERATED_TYPE, ASN1Integer.encodeIntValue(intValue));
069    
070        this.intValue = intValue;
071      }
072    
073    
074    
075      /**
076       * Creates a new ASN.1 enumerated element with the specified BER type and the
077       * provided int value.
078       *
079       * @param  type      The BER type to use for this element.
080       * @param  intValue  The int value to use for this element.
081       */
082      public ASN1Enumerated(final byte type, final int intValue)
083      {
084        super(type, ASN1Integer.encodeIntValue(intValue));
085    
086        this.intValue = intValue;
087      }
088    
089    
090    
091      /**
092       * Creates a new ASN.1 enumerated element with the specified BER type and the
093       * provided int and pre-encoded values.
094       *
095       * @param  type      The BER type to use for this element.
096       * @param  intValue  The int value to use for this element.
097       * @param  value     The pre-encoded value to use for this element.
098       */
099      private ASN1Enumerated(final byte type, final int intValue,
100                             final byte[] value)
101      {
102        super(type, value);
103    
104        this.intValue = intValue;
105      }
106    
107    
108    
109      /**
110       * Retrieves the int value for this element.
111       *
112       * @return  The int value for this element.
113       */
114      public int intValue()
115      {
116        return intValue;
117      }
118    
119    
120    
121      /**
122       * Decodes the contents of the provided byte array as an enumerated element.
123       *
124       * @param  elementBytes  The byte array to decode as an ASN.1 enumerated
125       *                       element.
126       *
127       * @return  The decoded ASN.1 enumerated element.
128       *
129       * @throws  ASN1Exception  If the provided array cannot be decoded as an
130       *                         enumerated element.
131       */
132      public static ASN1Enumerated decodeAsEnumerated(final byte[] elementBytes)
133             throws ASN1Exception
134      {
135        try
136        {
137          int valueStartPos = 2;
138          int length = (elementBytes[1] & 0x7F);
139          if (length != elementBytes[1])
140          {
141            final int numLengthBytes = length;
142    
143            length = 0;
144            for (int i=0; i < numLengthBytes; i++)
145            {
146              length <<= 8;
147              length |= (elementBytes[valueStartPos++] & 0xFF);
148            }
149          }
150    
151          if ((elementBytes.length - valueStartPos) != length)
152          {
153            throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
154                                         (elementBytes.length - valueStartPos)));
155          }
156    
157          final byte[] value = new byte[length];
158          System.arraycopy(elementBytes, valueStartPos, value, 0, length);
159    
160          int intValue;
161          switch (value.length)
162          {
163            case 1:
164              intValue = (value[0] & 0xFF);
165              if ((value[0] & 0x80) != 0x00)
166              {
167                intValue |= 0xFFFFFF00;
168              }
169              break;
170    
171            case 2:
172              intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
173              if ((value[0] & 0x80) != 0x00)
174              {
175                intValue |= 0xFFFF0000;
176              }
177              break;
178    
179            case 3:
180              intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
181                         (value[2] & 0xFF);
182              if ((value[0] & 0x80) != 0x00)
183              {
184                intValue |= 0xFF000000;
185              }
186              break;
187    
188            case 4:
189              intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
190                         ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
191              break;
192    
193            default:
194              throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get(
195                                           value.length));
196          }
197    
198          return new ASN1Enumerated(elementBytes[0], intValue, value);
199        }
200        catch (final ASN1Exception ae)
201        {
202          debugException(ae);
203          throw ae;
204        }
205        catch (final Exception e)
206        {
207          debugException(e);
208          throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
209        }
210      }
211    
212    
213    
214      /**
215       * Decodes the provided ASN.1 element as an enumerated element.
216       *
217       * @param  element  The ASN.1 element to be decoded.
218       *
219       * @return  The decoded ASN.1 enumerated element.
220       *
221       * @throws  ASN1Exception  If the provided element cannot be decoded as an
222       *                         enumerated element.
223       */
224      public static ASN1Enumerated decodeAsEnumerated(final ASN1Element element)
225             throws ASN1Exception
226      {
227        int intValue;
228        final byte[] value = element.getValue();
229        switch (value.length)
230        {
231          case 1:
232            intValue = (value[0] & 0xFF);
233            if ((value[0] & 0x80) != 0x00)
234            {
235              intValue |= 0xFFFFFF00;
236            }
237            break;
238    
239          case 2:
240            intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
241            if ((value[0] & 0x80) != 0x00)
242            {
243              intValue |= 0xFFFF0000;
244            }
245            break;
246    
247          case 3:
248            intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
249                       (value[2] & 0xFF);
250            if ((value[0] & 0x80) != 0x00)
251            {
252              intValue |= 0xFF000000;
253            }
254            break;
255    
256          case 4:
257            intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
258                       ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
259            break;
260    
261          default:
262            throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get(
263                                         value.length));
264        }
265    
266        return new ASN1Enumerated(element.getType(), intValue, value);
267      }
268    
269    
270    
271      /**
272       * Appends a string representation of this ASN.1 element to the provided
273       * buffer.
274       *
275       * @param  buffer  The buffer to which to append the information.
276       */
277      @Override()
278      public void toString(final StringBuilder buffer)
279      {
280        buffer.append(intValue);
281      }
282    }