001/*
002 * Copyright 2017-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2017-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2017-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.asn1;
037
038
039
040import com.unboundid.util.Debug;
041import com.unboundid.util.NotMutable;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045
046import static com.unboundid.asn1.ASN1Messages.*;
047
048
049
050/**
051 * This class provides an ASN.1 numeric string element that can hold any
052 * empty or non-empty string comprised only of the ASCII numeric digits '0'
053 * through '9' and the ASCII space.
054 */
055@NotMutable()
056@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
057public final class ASN1NumericString
058       extends ASN1Element
059{
060  /**
061   * The serial version UID for this serializable class.
062   */
063  private static final long serialVersionUID = -3972798266250943461L;
064
065
066
067  // The string value for this element.
068  private final String stringValue;
069
070
071
072  /**
073   * Creates a new ASN.1 numeric string element with the default BER type and
074   * the provided value.
075   *
076   * @param  stringValue  The string value to use for this element.  It may be
077   *                      {@code null} or empty if the value should be empty.
078   *                      It must only contain ASCII digit and space characters.
079   *
080   * @throws  ASN1Exception  If the provided string does not represent a valid
081   *                         numeric string.
082   */
083  public ASN1NumericString(final String stringValue)
084         throws ASN1Exception
085  {
086    this(ASN1Constants.UNIVERSAL_NUMERIC_STRING_TYPE, stringValue);
087  }
088
089
090
091  /**
092   * Creates a new ASN.1 numeric string element with the specified BER type
093   * and the provided value.
094   *
095   * @param  type         The BER type for this element.
096   * @param  stringValue  The string value to use for this element.  It may be
097   *                      {@code null} or empty if the value should be empty.
098   *                      It must only contain ASCII digit and space characters.
099   *
100   * @throws  ASN1Exception  If the provided string does not represent a valid
101   *                         numeric string.
102   */
103  public ASN1NumericString(final byte type, final String stringValue)
104         throws ASN1Exception
105  {
106    this(type, stringValue, StaticUtils.getBytes(stringValue));
107  }
108
109
110
111  /**
112   * Creates a new ASN.1 numeric string element with the specified BER type
113   * and the provided value.
114   *
115   * @param  type          The BER type for this element.
116   * @param  stringValue   The string value to use for this element.  It may be
117   *                       {@code null} or empty if the value should be empty.
118   *                       It must only contain ASCII digit and space
119   *                       characters.
120   * @param  encodedValue  The bytes that comprise the encoded element value.
121   *
122   * @throws  ASN1Exception  If the provided string does not represent a valid
123   *                         numeric string.
124   */
125  private ASN1NumericString(final byte type, final String stringValue,
126                            final byte[] encodedValue)
127          throws ASN1Exception
128  {
129    super(type, encodedValue);
130
131    if (stringValue == null)
132    {
133      this.stringValue = "";
134    }
135    else
136    {
137      this.stringValue = stringValue;
138      for (final char c : stringValue.toCharArray())
139      {
140        if ((c >= '0') && (c <= '9'))
141        {
142          // ASCII digits are allowed in numeric strings.
143        }
144        else if (c == ' ')
145        {
146          // The space is allowed in numeric strings.
147        }
148        else
149        {
150          throw new ASN1Exception(
151               ERR_NUMERIC_STRING_DECODE_VALUE_NOT_NUMERIC.get());
152        }
153      }
154    }
155  }
156
157
158
159  /**
160   * Retrieves the string value for this element.
161   *
162   * @return  The string value for this element.
163   */
164  public String stringValue()
165  {
166    return stringValue;
167  }
168
169
170
171  /**
172   * Decodes the contents of the provided byte array as a numeric string
173   * element.
174   *
175   * @param  elementBytes  The byte array to decode as an ASN.1 numeric string
176   *                       element.
177   *
178   * @return  The decoded ASN.1 numeric string element.
179   *
180   * @throws  ASN1Exception  If the provided array cannot be decoded as a
181   *                         numeric string element.
182   */
183  public static ASN1NumericString decodeAsNumericString(
184                                       final byte[] elementBytes)
185         throws ASN1Exception
186  {
187    try
188    {
189      int valueStartPos = 2;
190      int length = (elementBytes[1] & 0x7F);
191      if (length != elementBytes[1])
192      {
193        final int numLengthBytes = length;
194
195        length = 0;
196        for (int i=0; i < numLengthBytes; i++)
197        {
198          length <<= 8;
199          length |= (elementBytes[valueStartPos++] & 0xFF);
200        }
201      }
202
203      if ((elementBytes.length - valueStartPos) != length)
204      {
205        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
206                                     (elementBytes.length - valueStartPos)));
207      }
208
209      final byte[] elementValue = new byte[length];
210      System.arraycopy(elementBytes, valueStartPos, elementValue, 0, length);
211
212      return new ASN1NumericString(elementBytes[0],
213           StaticUtils.toUTF8String(elementValue), elementValue);
214    }
215    catch (final ASN1Exception ae)
216    {
217      Debug.debugException(ae);
218      throw ae;
219    }
220    catch (final Exception e)
221    {
222      Debug.debugException(e);
223      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
224    }
225  }
226
227
228
229  /**
230   * Decodes the provided ASN.1 element as a numeric string element.
231   *
232   * @param  element  The ASN.1 element to be decoded.
233   *
234   * @return  The decoded ASN.1 numeric string element.
235   *
236   * @throws  ASN1Exception  If the provided element cannot be decoded as a
237   *                         numeric string element.
238   */
239  public static ASN1NumericString decodeAsNumericString(
240                                       final ASN1Element element)
241         throws ASN1Exception
242  {
243    final byte[] elementValue = element.getValue();
244    return new ASN1NumericString(element.getType(),
245         StaticUtils.toUTF8String(elementValue), elementValue);
246  }
247
248
249
250  /**
251   * {@inheritDoc}
252   */
253  @Override()
254  public void toString(final StringBuilder buffer)
255  {
256    buffer.append(stringValue);
257  }
258}