001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 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 integer element, whose value may be represented
037     * as an integer with up to a 32-bit representation.
038     */
039    @NotMutable()
040    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
041    public final class ASN1Integer
042           extends ASN1Element
043    {
044      /**
045       * The serial version UID for this serializable class.
046       */
047      private static final long serialVersionUID = -733929804601994372L;
048    
049    
050    
051      // The int value for this element.
052      private final int intValue;
053    
054    
055    
056      /**
057       * Creates a new ASN.1 integer element with the default BER type and the
058       * provided int value.
059       *
060       * @param  intValue  The int value to use for this element.
061       */
062      public ASN1Integer(final int intValue)
063      {
064        super(UNIVERSAL_INTEGER_TYPE, encodeIntValue(intValue));
065    
066        this.intValue = intValue;
067      }
068    
069    
070    
071      /**
072       * Creates a new ASN.1 integer element with the specified BER type and the
073       * provided int value.
074       *
075       * @param  type      The BER type to use for this element.
076       * @param  intValue  The int value to use for this element.
077       */
078      public ASN1Integer(final byte type, final int intValue)
079      {
080        super(type, encodeIntValue(intValue));
081    
082        this.intValue = intValue;
083      }
084    
085    
086    
087      /**
088       * Creates a new ASN.1 integer element with the specified BER type and the
089       * provided int and pre-encoded values.
090       *
091       * @param  type      The BER type to use for this element.
092       * @param  intValue  The int value to use for this element.
093       * @param  value     The pre-encoded value to use for this element.
094       */
095      private ASN1Integer(final byte type, final int intValue, final byte[] value)
096      {
097        super(type, value);
098    
099        this.intValue = intValue;
100      }
101    
102    
103    
104      /**
105       * Encodes the provided int value to a byte array suitable for use as the
106       * value of an integer element.
107       *
108       * @param  intValue  The int value to be encoded.
109       *
110       * @return  A byte array containing the encoded value.
111       */
112      static byte[] encodeIntValue(final int intValue)
113      {
114        if (intValue < 0)
115        {
116          if ((intValue & 0xFFFFFF80) == 0xFFFFFF80)
117          {
118            return new byte[]
119            {
120              (byte) (intValue & 0xFF)
121            };
122          }
123          else if ((intValue & 0xFFFF8000) == 0xFFFF8000)
124          {
125            return new byte[]
126            {
127              (byte) ((intValue >> 8) & 0xFF),
128              (byte) (intValue & 0xFF)
129            };
130          }
131          else if ((intValue & 0xFF800000) == 0xFF800000)
132          {
133            return new byte[]
134            {
135              (byte) ((intValue >> 16) & 0xFF),
136              (byte) ((intValue >> 8) & 0xFF),
137              (byte) (intValue & 0xFF)
138            };
139          }
140          else
141          {
142            return new byte[]
143            {
144              (byte) ((intValue >> 24) & 0xFF),
145              (byte) ((intValue >> 16) & 0xFF),
146              (byte) ((intValue >> 8) & 0xFF),
147              (byte) (intValue & 0xFF)
148            };
149          }
150        }
151        else
152        {
153          if ((intValue & 0x0000007F) == intValue)
154          {
155            return new byte[]
156            {
157              (byte) (intValue & 0x7F)
158            };
159          }
160          else if ((intValue & 0x00007FFF) == intValue)
161          {
162            return new byte[]
163            {
164              (byte) ((intValue >> 8) & 0x7F),
165              (byte) (intValue & 0xFF)
166            };
167          }
168          else if ((intValue & 0x007FFFFF) == intValue)
169          {
170            return new byte[]
171            {
172              (byte) ((intValue >> 16) & 0x7F),
173              (byte) ((intValue >> 8) & 0xFF),
174              (byte) (intValue & 0xFF)
175            };
176          }
177          else
178          {
179            return new byte[]
180            {
181              (byte) ((intValue >> 24) & 0x7F),
182              (byte) ((intValue >> 16) & 0xFF),
183              (byte) ((intValue >> 8) & 0xFF),
184              (byte) (intValue & 0xFF)
185            };
186          }
187        }
188      }
189    
190    
191    
192      /**
193       * Retrieves the int value for this element.
194       *
195       * @return  The int value for this element.
196       */
197      public int intValue()
198      {
199        return intValue;
200      }
201    
202    
203    
204      /**
205       * Decodes the contents of the provided byte array as an integer element.
206       *
207       * @param  elementBytes  The byte array to decode as an ASN.1 integer element.
208       *
209       * @return  The decoded ASN.1 integer element.
210       *
211       * @throws  ASN1Exception  If the provided array cannot be decoded as an
212       *                         integer element.
213       */
214      public static ASN1Integer decodeAsInteger(final byte[] elementBytes)
215             throws ASN1Exception
216      {
217        try
218        {
219          int valueStartPos = 2;
220          int length = (elementBytes[1] & 0x7F);
221          if (length != elementBytes[1])
222          {
223            final int numLengthBytes = length;
224    
225            length = 0;
226            for (int i=0; i < numLengthBytes; i++)
227            {
228              length <<= 8;
229              length |= (elementBytes[valueStartPos++] & 0xFF);
230            }
231          }
232    
233          if ((elementBytes.length - valueStartPos) != length)
234          {
235            throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
236                                         (elementBytes.length - valueStartPos)));
237          }
238    
239          final byte[] value = new byte[length];
240          System.arraycopy(elementBytes, valueStartPos, value, 0, length);
241    
242          int intValue;
243          switch (value.length)
244          {
245            case 1:
246              intValue = (value[0] & 0xFF);
247              if ((value[0] & 0x80) != 0x00)
248              {
249                intValue |= 0xFFFFFF00;
250              }
251              break;
252    
253            case 2:
254              intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
255              if ((value[0] & 0x80) != 0x00)
256              {
257                intValue |= 0xFFFF0000;
258              }
259              break;
260    
261            case 3:
262              intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
263                         (value[2] & 0xFF);
264              if ((value[0] & 0x80) != 0x00)
265              {
266                intValue |= 0xFF000000;
267              }
268              break;
269    
270            case 4:
271              intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
272                         ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
273              break;
274    
275            default:
276              throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get(
277                                           value.length));
278          }
279    
280          return new ASN1Integer(elementBytes[0], intValue, value);
281        }
282        catch (final ASN1Exception ae)
283        {
284          debugException(ae);
285          throw ae;
286        }
287        catch (final Exception e)
288        {
289          debugException(e);
290          throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
291        }
292      }
293    
294    
295    
296      /**
297       * Decodes the provided ASN.1 element as an integer element.
298       *
299       * @param  element  The ASN.1 element to be decoded.
300       *
301       * @return  The decoded ASN.1 integer element.
302       *
303       * @throws  ASN1Exception  If the provided element cannot be decoded as an
304       *                         integer element.
305       */
306      public static ASN1Integer decodeAsInteger(final ASN1Element element)
307             throws ASN1Exception
308      {
309        int intValue;
310        final byte[] value = element.getValue();
311        switch (value.length)
312        {
313          case 1:
314            intValue = (value[0] & 0xFF);
315            if ((value[0] & 0x80) != 0x00)
316            {
317              intValue |= 0xFFFFFF00;
318            }
319            break;
320    
321          case 2:
322            intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
323            if ((value[0] & 0x80) != 0x00)
324            {
325              intValue |= 0xFFFF0000;
326            }
327            break;
328    
329          case 3:
330            intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
331                       (value[2] & 0xFF);
332            if ((value[0] & 0x80) != 0x00)
333            {
334              intValue |= 0xFF000000;
335            }
336            break;
337    
338          case 4:
339            intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
340                       ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
341            break;
342    
343          default:
344            throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(value.length));
345        }
346    
347        return new ASN1Integer(element.getType(), intValue, value);
348      }
349    
350    
351    
352      /**
353       * Appends a string representation of this ASN.1 element to the provided
354       * buffer.
355       *
356       * @param  buffer  The buffer to which to append the information.
357       */
358      @Override()
359      public void toString(final StringBuilder buffer)
360      {
361        buffer.append(intValue);
362      }
363    }