001/* 002 * Copyright 2017-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2019 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}