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        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
33    }
34
35    /**
36     * return a Printable String from a tagged object.
37     *
38     * @param obj the tagged object holding the object we want
39     * @param explicit true if the object is meant to be explicitly
40     *              tagged false otherwise.
41     * @exception IllegalArgumentException if the tagged object cannot
42     *               be converted.
43     */
44    public static DERPrintableString getInstance(
45        ASN1TaggedObject obj,
46        boolean          explicit)
47    {
48        ASN1Primitive o = obj.getObject();
49
50        if (explicit || o instanceof DERPrintableString)
51        {
52            return getInstance(o);
53        }
54        else
55        {
56            return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
57        }
58    }
59
60    /**
61     * basic constructor - byte encoded string.
62     */
63    DERPrintableString(
64        byte[]   string)
65    {
66        this.string = string;
67    }
68
69    /**
70     * basic constructor - this does not validate the string
71     */
72    public DERPrintableString(
73        String   string)
74    {
75        this(string, false);
76    }
77
78    /**
79     * Constructor with optional validation.
80     *
81     * @param string the base string to wrap.
82     * @param validate whether or not to check the string.
83     * @throws IllegalArgumentException if validate is true and the string
84     * contains characters that should not be in a PrintableString.
85     */
86    public DERPrintableString(
87        String   string,
88        boolean  validate)
89    {
90        if (validate && !isPrintableString(string))
91        {
92            throw new IllegalArgumentException("string contains illegal characters");
93        }
94
95        this.string = Strings.toByteArray(string);
96    }
97
98    public String getString()
99    {
100        return Strings.fromByteArray(string);
101    }
102
103    public byte[] getOctets()
104    {
105        return Arrays.clone(string);
106    }
107
108    boolean isConstructed()
109    {
110        return false;
111    }
112
113    int encodedLength()
114    {
115        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
116    }
117
118    void encode(
119        ASN1OutputStream out)
120        throws IOException
121    {
122        out.writeEncoded(BERTags.PRINTABLE_STRING, string);
123    }
124
125    public int hashCode()
126    {
127        return Arrays.hashCode(string);
128    }
129
130    boolean asn1Equals(
131        ASN1Primitive o)
132    {
133        if (!(o instanceof DERPrintableString))
134        {
135            return false;
136        }
137
138        DERPrintableString  s = (DERPrintableString)o;
139
140        return Arrays.areEqual(string, s.string);
141    }
142
143    public String toString()
144    {
145        return getString();
146    }
147
148    /**
149     * return true if the passed in String can be represented without
150     * loss as a PrintableString, false otherwise.
151     *
152     * @return true if in printable set, false otherwise.
153     */
154    public static boolean isPrintableString(
155        String  str)
156    {
157        for (int i = str.length() - 1; i >= 0; i--)
158        {
159            char    ch = str.charAt(i);
160
161            if (ch > 0x007f)
162            {
163                return false;
164            }
165
166            if ('a' <= ch && ch <= 'z')
167            {
168                continue;
169            }
170
171            if ('A' <= ch && ch <= 'Z')
172            {
173                continue;
174            }
175
176            if ('0' <= ch && ch <= '9')
177            {
178                continue;
179            }
180
181            switch (ch)
182            {
183            case ' ':
184            case '\'':
185            case '(':
186            case ')':
187            case '+':
188            case '-':
189            case '.':
190            case ':':
191            case '=':
192            case '?':
193            case '/':
194            case ',':
195                continue;
196            }
197
198            return false;
199        }
200
201        return true;
202    }
203}
204