1package org.bouncycastle.asn1;
2
3import java.io.IOException;
4
5/**
6 * DER PrintableString object.
7 */
8public class DERPrintableString
9    extends ASN1Object
10    implements DERString
11{
12    // BEGIN android-changed
13    private final String string;
14    // END android-changed
15
16    /**
17     * return a printable string from the passed in object.
18     *
19     * @exception IllegalArgumentException if the object cannot be converted.
20     */
21    public static DERPrintableString getInstance(
22        Object  obj)
23    {
24        if (obj == null || obj instanceof DERPrintableString)
25        {
26            return (DERPrintableString)obj;
27        }
28
29        if (obj instanceof ASN1OctetString)
30        {
31            return new DERPrintableString(((ASN1OctetString)obj).getOctets());
32        }
33
34        if (obj instanceof ASN1TaggedObject)
35        {
36            return getInstance(((ASN1TaggedObject)obj).getObject());
37        }
38
39        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
40    }
41
42    /**
43     * return a Printable String from a tagged object.
44     *
45     * @param obj the tagged object holding the object we want
46     * @param explicit true if the object is meant to be explicitly
47     *              tagged false otherwise.
48     * @exception IllegalArgumentException if the tagged object cannot
49     *               be converted.
50     */
51    public static DERPrintableString getInstance(
52        ASN1TaggedObject obj,
53        boolean          explicit)
54    {
55        return getInstance(obj.getObject());
56    }
57
58    /**
59     * basic constructor - byte encoded string.
60     */
61    public DERPrintableString(
62        byte[]   string)
63    {
64        char[]  cs = new char[string.length];
65
66        for (int i = 0; i != cs.length; i++)
67        {
68            cs[i] = (char)(string[i] & 0xff);
69        }
70
71        // BEGIN android-changed
72        this.string = new String(cs).intern();
73        // END android-changed
74    }
75
76    /**
77     * basic constructor - this does not validate the string
78     */
79    public DERPrintableString(
80        String   string)
81    {
82        this(string, false);
83    }
84
85    /**
86     * Constructor with optional validation.
87     *
88     * @param string the base string to wrap.
89     * @param validate whether or not to check the string.
90     * @throws IllegalArgumentException if validate is true and the string
91     * contains characters that should not be in a PrintableString.
92     */
93    public DERPrintableString(
94        String   string,
95        boolean  validate)
96    {
97        if (validate && !isPrintableString(string))
98        {
99            throw new IllegalArgumentException("string contains illegal characters");
100        }
101
102        // BEGIN android-changed
103        this.string = string.intern();
104        // END android-changed
105    }
106
107    public String getString()
108    {
109        return string;
110    }
111
112    public byte[] getOctets()
113    {
114        char[]  cs = string.toCharArray();
115        byte[]  bs = new byte[cs.length];
116
117        for (int i = 0; i != cs.length; i++)
118        {
119            bs[i] = (byte)cs[i];
120        }
121
122        return bs;
123    }
124
125    void encode(
126        DEROutputStream  out)
127        throws IOException
128    {
129        out.writeEncoded(PRINTABLE_STRING, this.getOctets());
130    }
131
132    public int hashCode()
133    {
134        return this.getString().hashCode();
135    }
136
137    boolean asn1Equals(
138        DERObject  o)
139    {
140        if (!(o instanceof DERPrintableString))
141        {
142            return false;
143        }
144
145        DERPrintableString  s = (DERPrintableString)o;
146
147        return this.getString().equals(s.getString());
148    }
149
150    public String toString()
151    {
152        return string;
153    }
154
155    /**
156     * return true if the passed in String can be represented without
157     * loss as a PrintableString, false otherwise.
158     *
159     * @return true if in printable set, false otherwise.
160     */
161    public static boolean isPrintableString(
162        String  str)
163    {
164        for (int i = str.length() - 1; i >= 0; i--)
165        {
166            char    ch = str.charAt(i);
167
168            if (ch > 0x007f)
169            {
170                return false;
171            }
172
173            if ('a' <= ch && ch <= 'z')
174            {
175                continue;
176            }
177
178            if ('A' <= ch && ch <= 'Z')
179            {
180                continue;
181            }
182
183            if ('0' <= ch && ch <= '9')
184            {
185                continue;
186            }
187
188            switch (ch)
189            {
190            case ' ':
191            case '\'':
192            case '(':
193            case ')':
194            case '+':
195            case '-':
196            case '.':
197            case ':':
198            case '=':
199            case '?':
200            case '/':
201            case ',':
202                continue;
203            }
204
205            return false;
206        }
207
208        return true;
209    }
210}
211