1package org.bouncycastle.asn1;
2
3import java.io.IOException;
4
5import org.bouncycastle.util.Arrays;
6import org.bouncycastle.util.Strings;
7
8/**
9 * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
10 */
11public class DERNumericString
12    extends ASN1Primitive
13    implements ASN1String
14{
15    private final byte[]  string;
16
17    /**
18     * return a Numeric string from the passed in object
19     *
20     * @param obj a DERNumericString or an object that can be converted into one.
21     * @exception IllegalArgumentException if the object cannot be converted.
22     * @return a DERNumericString instance, or null
23     */
24    public static DERNumericString getInstance(
25        Object  obj)
26    {
27        if (obj == null || obj instanceof DERNumericString)
28        {
29            return (DERNumericString)obj;
30        }
31
32        if (obj instanceof byte[])
33        {
34            try
35            {
36                return (DERNumericString)fromByteArray((byte[])obj);
37            }
38            catch (Exception e)
39            {
40                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
41            }
42        }
43
44        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
45    }
46
47    /**
48     * return an Numeric String from a tagged object.
49     *
50     * @param obj the tagged object holding the object we want
51     * @param explicit true if the object is meant to be explicitly
52     *              tagged false otherwise.
53     * @exception IllegalArgumentException if the tagged object cannot
54     *               be converted.
55     * @return a DERNumericString instance, or null.
56     */
57    public static DERNumericString getInstance(
58        ASN1TaggedObject obj,
59        boolean          explicit)
60    {
61        ASN1Primitive o = obj.getObject();
62
63        if (explicit || o instanceof DERNumericString)
64        {
65            return getInstance(o);
66        }
67        else
68        {
69            return new DERNumericString(ASN1OctetString.getInstance(o).getOctets());
70        }
71    }
72
73    /**
74     * basic constructor - with bytes.
75     */
76    DERNumericString(
77        byte[]   string)
78    {
79        this.string = string;
80    }
81
82    /**
83     * basic constructor -  without validation..
84     */
85    public DERNumericString(
86        String   string)
87    {
88        this(string, false);
89    }
90
91    /**
92     * Constructor with optional validation.
93     *
94     * @param string the base string to wrap.
95     * @param validate whether or not to check the string.
96     * @throws IllegalArgumentException if validate is true and the string
97     * contains characters that should not be in a NumericString.
98     */
99    public DERNumericString(
100        String   string,
101        boolean  validate)
102    {
103        if (validate && !isNumericString(string))
104        {
105            throw new IllegalArgumentException("string contains illegal characters");
106        }
107
108        this.string = Strings.toByteArray(string);
109    }
110
111    public String getString()
112    {
113        return Strings.fromByteArray(string);
114    }
115
116    public String toString()
117    {
118        return getString();
119    }
120
121    public byte[] getOctets()
122    {
123        return Arrays.clone(string);
124    }
125
126    boolean isConstructed()
127    {
128        return false;
129    }
130
131    int encodedLength()
132    {
133        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
134    }
135
136    void encode(
137        ASN1OutputStream out)
138        throws IOException
139    {
140        out.writeEncoded(BERTags.NUMERIC_STRING, string);
141    }
142
143    public int hashCode()
144    {
145        return Arrays.hashCode(string);
146    }
147
148    boolean asn1Equals(
149        ASN1Primitive o)
150    {
151        if (!(o instanceof DERNumericString))
152        {
153            return false;
154        }
155
156        DERNumericString  s = (DERNumericString)o;
157
158        return Arrays.areEqual(string, s.string);
159    }
160
161    /**
162     * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
163     *
164     * @param str string to validate.
165     * @return true if numeric, fale otherwise.
166     */
167    public static boolean isNumericString(
168        String  str)
169    {
170        for (int i = str.length() - 1; i >= 0; i--)
171        {
172            char    ch = str.charAt(i);
173
174            if (ch > 0x007f)
175            {
176                return false;
177            }
178
179            if (('0' <= ch && ch <= '9') || ch == ' ')
180            {
181                continue;
182            }
183
184            return false;
185        }
186
187        return true;
188    }
189}
190