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