001/*
002 * Copyright 2017-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2018 Ping Identity Corporation
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 */
021package com.unboundid.asn1;
022
023
024
025import com.unboundid.util.Debug;
026import com.unboundid.util.NotMutable;
027import com.unboundid.util.StaticUtils;
028import com.unboundid.util.ThreadSafety;
029import com.unboundid.util.ThreadSafetyLevel;
030
031import static com.unboundid.asn1.ASN1Messages.*;
032
033
034
035/**
036 * This class provides an ASN.1 numeric string element that can hold any
037 * empty or non-empty string comprised only of the ASCII numeric digits '0'
038 * through '9' and the ASCII space.
039 */
040@NotMutable()
041@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
042public final class ASN1NumericString
043       extends ASN1Element
044{
045  /**
046   * The serial version UID for this serializable class.
047   */
048  private static final long serialVersionUID = -3972798266250943461L;
049
050
051
052  // The string value for this element.
053  private final String stringValue;
054
055
056
057  /**
058   * Creates a new ASN.1 numeric string element with the default BER type and
059   * the provided value.
060   *
061   * @param  stringValue  The string value to use for this element.  It may be
062   *                      {@code null} or empty if the value should be empty.
063   *                      It must only contain ASCII digit and space characters.
064   *
065   * @throws  ASN1Exception  If the provided string does not represent a valid
066   *                         numeric string.
067   */
068  public ASN1NumericString(final String stringValue)
069         throws ASN1Exception
070  {
071    this(ASN1Constants.UNIVERSAL_NUMERIC_STRING_TYPE, stringValue);
072  }
073
074
075
076  /**
077   * Creates a new ASN.1 numeric string element with the specified BER type
078   * and the provided value.
079   *
080   * @param  type         The BER type for this element.
081   * @param  stringValue  The string value to use for this element.  It may be
082   *                      {@code null} or empty if the value should be empty.
083   *                      It must only contain ASCII digit and space characters.
084   *
085   * @throws  ASN1Exception  If the provided string does not represent a valid
086   *                         numeric string.
087   */
088  public ASN1NumericString(final byte type, final String stringValue)
089         throws ASN1Exception
090  {
091    this(type, stringValue, StaticUtils.getBytes(stringValue));
092  }
093
094
095
096  /**
097   * Creates a new ASN.1 numeric string element with the specified BER type
098   * and the provided value.
099   *
100   * @param  type          The BER type for this element.
101   * @param  stringValue   The string value to use for this element.  It may be
102   *                       {@code null} or empty if the value should be empty.
103   *                       It must only contain ASCII digit and space
104   *                       characters.
105   * @param  encodedValue  The bytes that comprise the encoded element value.
106   *
107   * @throws  ASN1Exception  If the provided string does not represent a valid
108   *                         numeric string.
109   */
110  private ASN1NumericString(final byte type, final String stringValue,
111                            final byte[] encodedValue)
112          throws ASN1Exception
113  {
114    super(type, encodedValue);
115
116    if (stringValue == null)
117    {
118      this.stringValue = "";
119    }
120    else
121    {
122      this.stringValue = stringValue;
123      for (final char c : stringValue.toCharArray())
124      {
125        if ((c >= '0') && (c <= '9'))
126        {
127          // ASCII digits are allowed in numeric strings.
128        }
129        else if (c == ' ')
130        {
131          // The space is allowed in numeric strings.
132        }
133        else
134        {
135          throw new ASN1Exception(
136               ERR_NUMERIC_STRING_DECODE_VALUE_NOT_NUMERIC.get());
137        }
138      }
139    }
140  }
141
142
143
144  /**
145   * Retrieves the string value for this element.
146   *
147   * @return  The string value for this element.
148   */
149  public String stringValue()
150  {
151    return stringValue;
152  }
153
154
155
156  /**
157   * Decodes the contents of the provided byte array as a numeric string
158   * element.
159   *
160   * @param  elementBytes  The byte array to decode as an ASN.1 numeric string
161   *                       element.
162   *
163   * @return  The decoded ASN.1 numeric string element.
164   *
165   * @throws  ASN1Exception  If the provided array cannot be decoded as a
166   *                         numeric string element.
167   */
168  public static ASN1NumericString decodeAsNumericString(
169                                       final byte[] elementBytes)
170         throws ASN1Exception
171  {
172    try
173    {
174      int valueStartPos = 2;
175      int length = (elementBytes[1] & 0x7F);
176      if (length != elementBytes[1])
177      {
178        final int numLengthBytes = length;
179
180        length = 0;
181        for (int i=0; i < numLengthBytes; i++)
182        {
183          length <<= 8;
184          length |= (elementBytes[valueStartPos++] & 0xFF);
185        }
186      }
187
188      if ((elementBytes.length - valueStartPos) != length)
189      {
190        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
191                                     (elementBytes.length - valueStartPos)));
192      }
193
194      final byte[] elementValue = new byte[length];
195      System.arraycopy(elementBytes, valueStartPos, elementValue, 0, length);
196
197      return new ASN1NumericString(elementBytes[0],
198           StaticUtils.toUTF8String(elementValue), elementValue);
199    }
200    catch (final ASN1Exception ae)
201    {
202      Debug.debugException(ae);
203      throw ae;
204    }
205    catch (final Exception e)
206    {
207      Debug.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 a numeric string element.
216   *
217   * @param  element  The ASN.1 element to be decoded.
218   *
219   * @return  The decoded ASN.1 numeric string element.
220   *
221   * @throws  ASN1Exception  If the provided element cannot be decoded as a
222   *                         numeric string element.
223   */
224  public static ASN1NumericString decodeAsNumericString(
225                                       final ASN1Element element)
226         throws ASN1Exception
227  {
228    final byte[] elementValue = element.getValue();
229    return new ASN1NumericString(element.getType(),
230         StaticUtils.toUTF8String(elementValue), elementValue);
231  }
232
233
234
235  /**
236   * {@inheritDoc}
237   */
238  @Override()
239  public void toString(final StringBuilder buffer)
240  {
241    buffer.append(stringValue);
242  }
243}