1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.luni.util;
19
20
21import java.io.ByteArrayOutputStream;
22import java.io.UTFDataFormatException;
23import java.io.UnsupportedEncodingException;
24import java.util.Calendar;
25import java.util.TimeZone;
26
27public final class Util {
28
29	private static String[] WEEKDAYS = new String[] { "", "Sunday", "Monday",
30			"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
31
32	private static String[] MONTHS = new String[] { "January", "February",
33			"March", "April", "May", "June", "July", "August", "September",
34			"October", "November", "December" };
35
36	private static final String defaultEncoding;
37
38	static {
39        // BEGIN android-changed
40        String encoding = System.getProperty("file.encoding");
41        // END android-changed
42		if (encoding != null) {
43			try {
44				"".getBytes(encoding);
45            } catch (Throwable t) {
46                encoding = null;
47			}
48		}
49        defaultEncoding = encoding;
50	}
51
52    /**
53     * Get bytes from String using default encoding; default encoding can
54     * be changed via "os.encoding" property
55     * @param name input String
56     * @return byte array
57     */
58    public static byte[] getBytes(String name) {
59		if (defaultEncoding != null) {
60			try {
61				return name.getBytes(defaultEncoding);
62			} catch (java.io.UnsupportedEncodingException e) {
63			}
64		}
65        return name.getBytes();
66	}
67
68    /**
69     * Get bytes from String with UTF8 encoding
70     * @param name
71     *          input String
72     * @return byte array
73     */
74    public static byte[] getUTF8Bytes(String name) {
75        try {
76            return name.getBytes("UTF-8");
77        } catch (java.io.UnsupportedEncodingException e) {
78            return getBytes(name);
79        }
80    }
81
82	public static String toString(byte[] bytes) {
83		if (defaultEncoding != null) {
84			try {
85				return new String(bytes, 0, bytes.length, defaultEncoding);
86			} catch (java.io.UnsupportedEncodingException e) {
87			}
88		}
89		return new String(bytes, 0, bytes.length);
90	}
91
92    public static String toUTF8String(byte[] bytes) {
93        return toUTF8String(bytes, 0, bytes.length);
94    }
95
96	public static String toString(byte[] bytes, int offset, int length) {
97		if (defaultEncoding != null) {
98			try {
99				return new String(bytes, offset, length, defaultEncoding);
100			} catch (java.io.UnsupportedEncodingException e) {
101			}
102		}
103		return new String(bytes, offset, length);
104	}
105
106    public static String toUTF8String(byte[] bytes, int offset, int length) {
107        try {
108            return new String(bytes, offset, length, "UTF-8");
109        } catch (java.io.UnsupportedEncodingException e) {
110            return toString(bytes, offset, length);
111        }
112    }
113
114	/**
115	 * Answers the millisecond value of the date and time parsed from the
116	 * specified String. Many date/time formats are recognized
117	 *
118	 * @param string
119	 *            the String to parse
120	 * @return the millisecond value parsed from the String
121	 */
122	public static long parseDate(String string) {
123		int offset = 0, length = string.length(), state = 0;
124		int year = -1, month = -1, date = -1;
125		int hour = -1, minute = -1, second = -1;
126		final int PAD = 0, LETTERS = 1, NUMBERS = 2;
127		StringBuilder buffer = new StringBuilder();
128
129		while (offset <= length) {
130			char next = offset < length ? string.charAt(offset) : '\r';
131			offset++;
132
133			int nextState;
134			if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))
135				nextState = LETTERS;
136			else if (next >= '0' && next <= '9')
137				nextState = NUMBERS;
138			else if (" ,-:\r\t".indexOf(next) == -1)
139				throw new IllegalArgumentException();
140			else
141				nextState = PAD;
142
143			if (state == NUMBERS && nextState != NUMBERS) {
144				int digit = Integer.parseInt(buffer.toString());
145				buffer.setLength(0);
146				if (digit >= 70) {
147					if (year != -1
148							|| (next != ' ' && next != ',' && next != '\r'))
149						throw new IllegalArgumentException();
150					year = digit;
151				} else if (next == ':') {
152					if (hour == -1)
153						hour = digit;
154					else if (minute == -1)
155						minute = digit;
156					else
157						throw new IllegalArgumentException();
158				} else if (next == ' ' || next == ',' || next == '-'
159						|| next == '\r') {
160					if (hour != -1 && minute == -1)
161						minute = digit;
162					else if (minute != -1 && second == -1)
163						second = digit;
164					else if (date == -1)
165						date = digit;
166					else if (year == -1)
167						year = digit;
168					else
169						throw new IllegalArgumentException();
170				} else if (year == -1 && month != -1 && date != -1)
171					year = digit;
172				else
173					throw new IllegalArgumentException();
174			} else if (state == LETTERS && nextState != LETTERS) {
175				String text = buffer.toString().toUpperCase();
176				buffer.setLength(0);
177				if (text.length() < 3)
178					throw new IllegalArgumentException();
179				if (parse(text, WEEKDAYS) != -1) {
180				} else if (month == -1 && (month = parse(text, MONTHS)) != -1) {
181				} else if (text.equals("GMT")) {
182				} else
183					throw new IllegalArgumentException();
184			}
185
186			if (nextState == LETTERS || nextState == NUMBERS)
187				buffer.append(next);
188			state = nextState;
189		}
190
191		if (year != -1 && month != -1 && date != -1) {
192			if (hour == -1)
193				hour = 0;
194			if (minute == -1)
195				minute = 0;
196			if (second == -1)
197				second = 0;
198			Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
199			int current = cal.get(Calendar.YEAR) - 80;
200			if (year < 100) {
201				year += current / 100 * 100;
202				if (year < current)
203					year += 100;
204			}
205			cal.set(Calendar.YEAR, year);
206			cal.set(Calendar.MONTH, month);
207			cal.set(Calendar.DATE, date);
208			cal.set(Calendar.HOUR_OF_DAY, hour);
209			cal.set(Calendar.MINUTE, minute);
210			cal.set(Calendar.SECOND, second);
211			cal.set(Calendar.MILLISECOND, 0);
212			return cal.getTime().getTime();
213		}
214		throw new IllegalArgumentException();
215	}
216
217	private static int parse(String string, String[] array) {
218		int length = string.length();
219		for (int i = 0; i < array.length; i++) {
220			if (string.regionMatches(true, 0, array[i], 0, length))
221				return i;
222		}
223		return -1;
224	}
225
226	public static String convertFromUTF8(byte[] buf, int offset, int utfSize)
227			throws UTFDataFormatException {
228		return convertUTF8WithBuf(buf, new char[utfSize], offset, utfSize);
229	}
230
231	public static String convertUTF8WithBuf(byte[] buf, char[] out, int offset,
232			int utfSize) throws UTFDataFormatException {
233		int count = 0, s = 0, a;
234		while (count < utfSize) {
235			if ((out[s] = (char) buf[offset + count++]) < '\u0080')
236				s++;
237			else if (((a = out[s]) & 0xe0) == 0xc0) {
238				if (count >= utfSize)
239					throw new UTFDataFormatException(Msg.getString("K0062",
240							count));
241                // BEGIN android-changed
242                int b = buf[offset + count++];
243                // END android-changed
244				if ((b & 0xC0) != 0x80)
245					throw new UTFDataFormatException(Msg.getString("K0062",
246							(count - 1)));
247				out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
248			} else if ((a & 0xf0) == 0xe0) {
249				if (count + 1 >= utfSize)
250					throw new UTFDataFormatException(Msg.getString("K0063",
251							(count + 1)));
252                // BEGIN android-changed
253                int b = buf[offset + count++];
254                int c = buf[offset + count++];
255                // END android-changed
256				if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80))
257					throw new UTFDataFormatException(Msg.getString("K0064",
258							(count - 2)));
259				out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
260			} else {
261				throw new UTFDataFormatException(Msg.getString("K0065",
262						(count - 1)));
263			}
264		}
265		return new String(out, 0, s);
266	}
267
268	/**
269	 * '%' and two following hex digit characters are converted to the
270	 * equivalent byte value. All other characters are passed through
271	 * unmodified. e.g. "ABC %24%25" -> "ABC $%"
272	 *
273	 * @param s
274	 *            java.lang.String The encoded string.
275	 * @return java.lang.String The decoded version.
276	 */
277    public static String decode(String s, boolean convertPlus) {
278        return decode(s, convertPlus, null);
279    }
280
281    /**
282     * '%' and two following hex digit characters are converted to the
283     * equivalent byte value. All other characters are passed through
284     * unmodified. e.g. "ABC %24%25" -> "ABC $%"
285     *
286     * @param s
287     *            java.lang.String The encoded string.
288     * @param encoding
289     *            the specified encoding
290     * @return java.lang.String The decoded version.
291     */
292    public static String decode(String s, boolean convertPlus, String encoding) {
293        if (!convertPlus && s.indexOf('%') == -1)
294            return s;
295        StringBuilder result = new StringBuilder(s.length());
296        ByteArrayOutputStream out = new ByteArrayOutputStream();
297        for (int i = 0; i < s.length();) {
298            char c = s.charAt(i);
299            if (convertPlus && c == '+')
300                result.append(' ');
301            else if (c == '%') {
302                out.reset();
303                do {
304                    if (i + 2 >= s.length())
305                        throw new IllegalArgumentException(Msg.getString(
306                                "K01fe", i));
307                    int d1 = Character.digit(s.charAt(i + 1), 16);
308                    int d2 = Character.digit(s.charAt(i + 2), 16);
309                    if (d1 == -1 || d2 == -1)
310                        throw new IllegalArgumentException(Msg.getString(
311                                "K01ff", s.substring(i, i + 3), String
312                                        .valueOf(i)));
313                    out.write((byte) ((d1 << 4) + d2));
314                    i += 3;
315                } while (i < s.length() && s.charAt(i) == '%');
316                if (encoding == null) {
317                    result.append(out.toString());
318                } else {
319                    try {
320                        result.append(out.toString(encoding));
321                    } catch (UnsupportedEncodingException e) {
322                        throw new IllegalArgumentException(e);
323                    }
324                }
325                continue;
326            } else
327                result.append(c);
328            i++;
329        }
330        return result.toString();
331    }
332
333	public static String toASCIILowerCase(String s) {
334        int len = s.length();
335		StringBuilder buffer = new StringBuilder(len);
336		for (int i = 0; i < len; i++) {
337			char c = s.charAt(i);
338			if ('A' <= c && c <= 'Z') {
339				buffer.append((char) (c + ('a' - 'A')));
340			} else {
341				buffer.append(c);
342			}
343		}
344		return buffer.toString();
345	}
346
347	public static String toASCIIUpperCase(String s) {
348        int len = s.length();
349        StringBuilder buffer = new StringBuilder(len);
350        for (int i = 0; i < len; i++) {
351			char c = s.charAt(i);
352	        if ('a' <= c && c <= 'z') {
353	        	buffer.append((char) (c - ('a' - 'A')));
354	        } else {
355				buffer.append(c);
356			}
357		}
358		return buffer.toString();
359	}
360}
361