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