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    private final byte[]  string;
16
17    /**
18     * return a printable string from the passed in object.
19     *
20     * @param obj a DERPrintableString or an object that can be converted into one.
21     * @exception IllegalArgumentException if the object cannot be converted.
22     * @return a DERPrintableString instance, or null.
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     * @return a DERPrintableString instance, or null.
56     */
57    public static DERPrintableString getInstance(
58        ASN1TaggedObject obj,
59        boolean          explicit)
60    {
61        ASN1Primitive o = obj.getObject();
62
63        if (explicit || o instanceof DERPrintableString)
64        {
65            return getInstance(o);
66        }
67        else
68        {
69            return new DERPrintableString(ASN1OctetString.getInstance(o).getOctets());
70        }
71    }
72
73    /**
74     * basic constructor - byte encoded string.
75     */
76    DERPrintableString(
77        byte[]   string)
78    {
79        this.string = string;
80    }
81
82    /**
83     * basic constructor - this does not validate the string
84     */
85    public DERPrintableString(
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 PrintableString.
98     */
99    public DERPrintableString(
100        String   string,
101        boolean  validate)
102    {
103        if (validate && !isPrintableString(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 byte[] getOctets()
117    {
118        return Arrays.clone(string);
119    }
120
121    boolean isConstructed()
122    {
123        return false;
124    }
125
126    int encodedLength()
127    {
128        return 1 + StreamUtil.calculateBodyLength(string.length) + string.length;
129    }
130
131    void encode(
132        ASN1OutputStream out)
133        throws IOException
134    {
135        out.writeEncoded(BERTags.PRINTABLE_STRING, string);
136    }
137
138    public int hashCode()
139    {
140        return Arrays.hashCode(string);
141    }
142
143    boolean asn1Equals(
144        ASN1Primitive o)
145    {
146        if (!(o instanceof DERPrintableString))
147        {
148            return false;
149        }
150
151        DERPrintableString  s = (DERPrintableString)o;
152
153        return Arrays.areEqual(string, s.string);
154    }
155
156    public String toString()
157    {
158        return getString();
159    }
160
161    /**
162     * return true if the passed in String can be represented without
163     * loss as a PrintableString, false otherwise.
164     *
165     * @return true if in printable set, false otherwise.
166     */
167    public static boolean isPrintableString(
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 ('a' <= ch && ch <= 'z')
180            {
181                continue;
182            }
183
184            if ('A' <= ch && ch <= 'Z')
185            {
186                continue;
187            }
188
189            if ('0' <= ch && ch <= '9')
190            {
191                continue;
192            }
193
194            switch (ch)
195            {
196            case ' ':
197            case '\'':
198            case '(':
199            case ')':
200            case '+':
201            case '-':
202            case '.':
203            case ':':
204            case '=':
205            case '?':
206            case '/':
207            case ',':
208                continue;
209            }
210
211            return false;
212        }
213
214        return true;
215    }
216}
217