1package org.bouncycastle.util;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.io.OutputStream;
6import java.security.AccessController;
7import java.security.PrivilegedAction;
8import java.util.ArrayList;
9import java.util.Vector;
10
11/**
12 * String utilities.
13 */
14public final class Strings
15{
16    private static String LINE_SEPARATOR;
17
18    static
19    {
20       try
21       {
22           LINE_SEPARATOR = AccessController.doPrivileged(new PrivilegedAction<String>()
23           {
24               public String run()
25               {
26                   // the easy way
27                   return System.getProperty("line.separator");
28               }
29           });
30
31       }
32       catch (Exception e)
33       {
34           try
35           {
36               // the harder way
37               LINE_SEPARATOR = String.format("%n");
38           }
39           catch (Exception ef)
40           {
41               LINE_SEPARATOR = "\n";   // we're desperate use this...
42           }
43       }
44    }
45
46    public static String fromUTF8ByteArray(byte[] bytes)
47    {
48        int i = 0;
49        int length = 0;
50
51        while (i < bytes.length)
52        {
53            length++;
54            if ((bytes[i] & 0xf0) == 0xf0)
55            {
56                // surrogate pair
57                length++;
58                i += 4;
59            }
60            else if ((bytes[i] & 0xe0) == 0xe0)
61            {
62                i += 3;
63            }
64            else if ((bytes[i] & 0xc0) == 0xc0)
65            {
66                i += 2;
67            }
68            else
69            {
70                i += 1;
71            }
72        }
73
74        char[] cs = new char[length];
75
76        i = 0;
77        length = 0;
78
79        while (i < bytes.length)
80        {
81            char ch;
82
83            if ((bytes[i] & 0xf0) == 0xf0)
84            {
85                int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i + 1] & 0x3F) << 12) | ((bytes[i + 2] & 0x3F) << 6) | (bytes[i + 3] & 0x3F);
86                int U = codePoint - 0x10000;
87                char W1 = (char)(0xD800 | (U >> 10));
88                char W2 = (char)(0xDC00 | (U & 0x3FF));
89                cs[length++] = W1;
90                ch = W2;
91                i += 4;
92            }
93            else if ((bytes[i] & 0xe0) == 0xe0)
94            {
95                ch = (char)(((bytes[i] & 0x0f) << 12)
96                    | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
97                i += 3;
98            }
99            else if ((bytes[i] & 0xd0) == 0xd0)
100            {
101                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
102                i += 2;
103            }
104            else if ((bytes[i] & 0xc0) == 0xc0)
105            {
106                ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
107                i += 2;
108            }
109            else
110            {
111                ch = (char)(bytes[i] & 0xff);
112                i += 1;
113            }
114
115            cs[length++] = ch;
116        }
117
118        return new String(cs);
119    }
120
121    public static byte[] toUTF8ByteArray(String string)
122    {
123        return toUTF8ByteArray(string.toCharArray());
124    }
125
126    public static byte[] toUTF8ByteArray(char[] string)
127    {
128        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
129
130        try
131        {
132            toUTF8ByteArray(string, bOut);
133        }
134        catch (IOException e)
135        {
136            throw new IllegalStateException("cannot encode string to byte array!");
137        }
138
139        return bOut.toByteArray();
140    }
141
142    public static void toUTF8ByteArray(char[] string, OutputStream sOut)
143        throws IOException
144    {
145        char[] c = string;
146        int i = 0;
147
148        while (i < c.length)
149        {
150            char ch = c[i];
151
152            if (ch < 0x0080)
153            {
154                sOut.write(ch);
155            }
156            else if (ch < 0x0800)
157            {
158                sOut.write(0xc0 | (ch >> 6));
159                sOut.write(0x80 | (ch & 0x3f));
160            }
161            // surrogate pair
162            else if (ch >= 0xD800 && ch <= 0xDFFF)
163            {
164                // in error - can only happen, if the Java String class has a
165                // bug.
166                if (i + 1 >= c.length)
167                {
168                    throw new IllegalStateException("invalid UTF-16 codepoint");
169                }
170                char W1 = ch;
171                ch = c[++i];
172                char W2 = ch;
173                // in error - can only happen, if the Java String class has a
174                // bug.
175                if (W1 > 0xDBFF)
176                {
177                    throw new IllegalStateException("invalid UTF-16 codepoint");
178                }
179                int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000;
180                sOut.write(0xf0 | (codePoint >> 18));
181                sOut.write(0x80 | ((codePoint >> 12) & 0x3F));
182                sOut.write(0x80 | ((codePoint >> 6) & 0x3F));
183                sOut.write(0x80 | (codePoint & 0x3F));
184            }
185            else
186            {
187                sOut.write(0xe0 | (ch >> 12));
188                sOut.write(0x80 | ((ch >> 6) & 0x3F));
189                sOut.write(0x80 | (ch & 0x3F));
190            }
191
192            i++;
193        }
194    }
195
196    /**
197     * A locale independent version of toUpperCase.
198     *
199     * @param string input to be converted
200     * @return a US Ascii uppercase version
201     */
202    public static String toUpperCase(String string)
203    {
204        boolean changed = false;
205        char[] chars = string.toCharArray();
206
207        for (int i = 0; i != chars.length; i++)
208        {
209            char ch = chars[i];
210            if ('a' <= ch && 'z' >= ch)
211            {
212                changed = true;
213                chars[i] = (char)(ch - 'a' + 'A');
214            }
215        }
216
217        if (changed)
218        {
219            return new String(chars);
220        }
221
222        return string;
223    }
224
225    /**
226     * A locale independent version of toLowerCase.
227     *
228     * @param string input to be converted
229     * @return a US ASCII lowercase version
230     */
231    public static String toLowerCase(String string)
232    {
233        boolean changed = false;
234        char[] chars = string.toCharArray();
235
236        for (int i = 0; i != chars.length; i++)
237        {
238            char ch = chars[i];
239            if ('A' <= ch && 'Z' >= ch)
240            {
241                changed = true;
242                chars[i] = (char)(ch - 'A' + 'a');
243            }
244        }
245
246        if (changed)
247        {
248            return new String(chars);
249        }
250
251        return string;
252    }
253
254    public static byte[] toByteArray(char[] chars)
255    {
256        byte[] bytes = new byte[chars.length];
257
258        for (int i = 0; i != bytes.length; i++)
259        {
260            bytes[i] = (byte)chars[i];
261        }
262
263        return bytes;
264    }
265
266    public static byte[] toByteArray(String string)
267    {
268        byte[] bytes = new byte[string.length()];
269
270        for (int i = 0; i != bytes.length; i++)
271        {
272            char ch = string.charAt(i);
273
274            bytes[i] = (byte)ch;
275        }
276
277        return bytes;
278    }
279
280    public static int toByteArray(String s, byte[] buf, int off)
281    {
282        int count = s.length();
283        for (int i = 0; i < count; ++i)
284        {
285            char c = s.charAt(i);
286            buf[off + i] = (byte)c;
287        }
288        return count;
289    }
290
291    /**
292     * Convert an array of 8 bit characters into a string.
293     *
294     * @param bytes 8 bit characters.
295     * @return resulting String.
296     */
297    public static String fromByteArray(byte[] bytes)
298    {
299        return new String(asCharArray(bytes));
300    }
301
302    /**
303     * Do a simple conversion of an array of 8 bit characters into a string.
304     *
305     * @param bytes 8 bit characters.
306     * @return resulting String.
307     */
308    public static char[] asCharArray(byte[] bytes)
309    {
310        char[] chars = new char[bytes.length];
311
312        for (int i = 0; i != chars.length; i++)
313        {
314            chars[i] = (char)(bytes[i] & 0xff);
315        }
316
317        return chars;
318    }
319
320    public static String[] split(String input, char delimiter)
321    {
322        Vector v = new Vector();
323        boolean moreTokens = true;
324        String subString;
325
326        while (moreTokens)
327        {
328            int tokenLocation = input.indexOf(delimiter);
329            if (tokenLocation > 0)
330            {
331                subString = input.substring(0, tokenLocation);
332                v.addElement(subString);
333                input = input.substring(tokenLocation + 1);
334            }
335            else
336            {
337                moreTokens = false;
338                v.addElement(input);
339            }
340        }
341
342        String[] res = new String[v.size()];
343
344        for (int i = 0; i != res.length; i++)
345        {
346            res[i] = (String)v.elementAt(i);
347        }
348        return res;
349    }
350
351    public static StringList newList()
352    {
353        return new StringListImpl();
354    }
355
356    public static String lineSeparator()
357    {
358        return LINE_SEPARATOR;
359    }
360
361    private static class StringListImpl
362        extends ArrayList<String>
363        implements StringList
364    {
365        public boolean add(String s)
366        {
367            return super.add(s);
368        }
369
370        public String set(int index, String element)
371        {
372            return super.set(index, element);
373        }
374
375        public void add(int index, String element)
376        {
377            super.add(index, element);
378        }
379
380        public String[] toStringArray()
381        {
382            String[] strs = new String[this.size()];
383
384            for (int i = 0; i != strs.length; i++)
385            {
386                strs[i] = this.get(i);
387            }
388
389            return strs;
390        }
391
392        public String[] toStringArray(int from, int to)
393        {
394            String[] strs = new String[to - from];
395
396            for (int i = from; i != this.size() && i != to; i++)
397            {
398                strs[i - from] = this.get(i);
399            }
400
401            return strs;
402        }
403    }
404}
405