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