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 UTF-8 string element that can hold any string
052 * value that can be represented in the UTF-8 encoding.
053 */
054@NotMutable()
055@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
056public final class ASN1UTF8String
057       extends ASN1Element
058{
059  /**
060   * The serial version UID for this serializable class.
061   */
062  private static final long serialVersionUID = -2147537370903003997L;
063
064
065
066  // The string value for this element.
067  private final String stringValue;
068
069
070
071  /**
072   * Creates a new ASN.1 UTF-8 string element with the default BER type and the
073   * provided value.
074   *
075   * @param  stringValue  The string value to use for this element.  It may be
076   *                      {@code null} or empty if the value should be empty.
077   */
078  public ASN1UTF8String(final String stringValue)
079  {
080    this(ASN1Constants.UNIVERSAL_UTF_8_STRING_TYPE, stringValue);
081  }
082
083
084
085  /**
086   * Creates a new ASN.1 UTF-8 string element with the specified BER type and
087   * the provided value.
088   *
089   * @param  type         The BER type for this element.
090   * @param  stringValue  The string value to use for this element.  It may be
091   *                      {@code null} or empty if the value should be empty.
092   */
093  public ASN1UTF8String(final byte type, final String stringValue)
094  {
095    this(type, stringValue, StaticUtils.getBytes(stringValue));
096  }
097
098
099
100  /**
101   * Creates a new ASN.1 UTF-8 string element with the specified BER type and
102   * the provided value.
103   *
104   * @param  type          The BER type for this element.
105   * @param  stringValue   The string value to use for this element.  It may be
106   *                       {@code null} or empty if the value should be empty.
107   * @param  encodedValue  The encoded representation of the value.
108   */
109  private ASN1UTF8String(final byte type, final String stringValue,
110                         final byte[] encodedValue)
111  {
112    super(type, encodedValue);
113
114    if (stringValue == null)
115    {
116      this.stringValue = "";
117    }
118    else
119    {
120      this.stringValue = stringValue;
121    }
122  }
123
124
125
126  /**
127   * Retrieves the string value for this element.
128   *
129   * @return  The string value for this element.
130   */
131  public String stringValue()
132  {
133    return stringValue;
134  }
135
136
137
138  /**
139   * Decodes the contents of the provided byte array as a UTF-8 string element.
140   *
141   * @param  elementBytes  The byte array to decode as an ASN.1 UTF-8 string
142   *                       element.
143   *
144   * @return  The decoded ASN.1 UTF-8 string element.
145   *
146   * @throws  ASN1Exception  If the provided array cannot be decoded as a UTF-8
147   *                         string element.
148   */
149  public static ASN1UTF8String decodeAsUTF8String(final byte[] elementBytes)
150         throws ASN1Exception
151  {
152    try
153    {
154      int valueStartPos = 2;
155      int length = (elementBytes[1] & 0x7F);
156      if (length != elementBytes[1])
157      {
158        final int numLengthBytes = length;
159
160        length = 0;
161        for (int i=0; i < numLengthBytes; i++)
162        {
163          length <<= 8;
164          length |= (elementBytes[valueStartPos++] & 0xFF);
165        }
166      }
167
168      if ((elementBytes.length - valueStartPos) != length)
169      {
170        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
171                                     (elementBytes.length - valueStartPos)));
172      }
173
174      final byte[] elementValue = new byte[length];
175      System.arraycopy(elementBytes, valueStartPos, elementValue, 0, length);
176
177      if (! StaticUtils.isValidUTF8(elementValue))
178      {
179        throw new ASN1Exception(ERR_UTF_8_STRING_DECODE_VALUE_NOT_UTF_8.get());
180      }
181
182      return new ASN1UTF8String(elementBytes[0],
183           StaticUtils.toUTF8String(elementValue), elementValue);
184    }
185    catch (final ASN1Exception ae)
186    {
187      Debug.debugException(ae);
188      throw ae;
189    }
190    catch (final Exception e)
191    {
192      Debug.debugException(e);
193      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
194    }
195  }
196
197
198
199  /**
200   * Decodes the provided ASN.1 element as a UTF-8 string element.
201   *
202   * @param  element  The ASN.1 element to be decoded.
203   *
204   * @return  The decoded ASN.1 UTF-8 string element.
205   *
206   * @throws  ASN1Exception  If the provided element cannot be decoded as a
207   *                         UTF-8 string element.
208   */
209  public static ASN1UTF8String decodeAsUTF8String(final ASN1Element element)
210         throws ASN1Exception
211  {
212    final byte[] elementValue = element.getValue();
213    if (! StaticUtils.isValidUTF8(elementValue))
214    {
215      throw new ASN1Exception(ERR_UTF_8_STRING_DECODE_VALUE_NOT_UTF_8.get());
216    }
217
218    return new ASN1UTF8String(element.getType(),
219         StaticUtils.toUTF8String(elementValue), elementValue);
220  }
221
222
223
224  /**
225   * {@inheritDoc}
226   */
227  @Override()
228  public void toString(final StringBuilder buffer)
229  {
230    buffer.append(stringValue);
231  }
232}