001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: Locale.java,v 1.7 2009/07/07 17:32:02 bina Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted 2014-2015 ForgeRock AS. 031 * Portions Copyrighted 2014 Nomura Research Institute, Ltd. 032 */ 033package com.sun.identity.shared.locale; 034 035import com.sun.identity.shared.Constants; 036import com.sun.identity.shared.configuration.SystemPropertiesManager; 037import com.sun.identity.shared.debug.Debug; 038import java.io.UnsupportedEncodingException; 039import java.text.MessageFormat; 040import java.text.ParsePosition; 041import java.text.SimpleDateFormat; 042import java.util.BitSet; 043import java.util.Date; 044import java.util.ResourceBundle; 045import java.util.StringTokenizer; 046 047/** 048 * This class <code>Locale.java</code> is a utility that provides 049 * functionality for applications and services to internationalize their 050 * messages. 051 * @supported.all.api 052 */ 053public class Locale { 054 static BitSet dontEncode; 055 056 static final int caseDiff = ('a' - 'A'); 057 058 private static final int LOCALE_STRING_MAX_LEN = 5; 059 060 static java.util.Locale defaultLocale; 061 062 static Debug debug; 063 064 protected static final String USER_PROPERTIES = "amUser"; 065 066 protected static final String DATE_SYNTAX = "dateSyntax"; 067 068 private static final String normalizedDateString = "yyyy/MM/dd HH:mm:ss"; 069 070 private static final SimpleDateFormat normalizedDateFormat; 071 072 private static final String UNDERSCORE = "_"; 073 074 private static final String HYPHEN = "-"; 075 076 077 /* 078 * The list of characters that are not encoded have been determined by 079 * referencing O'Reilly's "HTML: The Definitive Guide" (page 164). 080 */ 081 082 static { 083 // Intialize static variables 084 debug = Debug.getInstance("amUtil"); 085 086 dontEncode = new BitSet(256); 087 int i; 088 for (i = 'a'; i <= 'z'; i++) { 089 dontEncode.set(i); 090 } 091 for (i = 'A'; i <= 'Z'; i++) { 092 dontEncode.set(i); 093 } 094 for (i = '0'; i <= '9'; i++) { 095 dontEncode.set(i); 096 } 097 dontEncode.set(' '); /* 098 * encoding a space to a + is done in the 099 * encode() method 100 */ 101 dontEncode.set('-'); 102 dontEncode.set('_'); 103 dontEncode.set('.'); 104 dontEncode.set('*'); 105 106 String loc = SystemPropertiesManager.get(Constants.AM_LOCALE, "en_US"); 107 defaultLocale = getLocale(loc); 108 normalizedDateFormat = new SimpleDateFormat(normalizedDateString); 109 } 110 111 public static void main(String[] args) { 112 System.out.println(":" + Locale.getLocale(args[0]) + ":"); 113 System.out.println(":" + Locale.getLocale(args[0]).getCountry() + ":"); 114 } 115 116 /** 117 * Gets the locale object for the specified localized string format. 118 * 119 * @param stringformat 120 * String representation of the locale. Examples: 121 * <code>en_US, en_UK, ja_JP</code>. 122 * @return the <code>java.util.locale</code> object. 123 */ 124 public static java.util.Locale getLocale(String stringformat) { 125 java.util.Locale locale = java.util.Locale.getDefault(); 126 if (stringformat == null) { 127 return locale; 128 } 129 130 StringTokenizer tk = null; 131 String lang = ""; 132 String country = ""; 133 String variant = ""; 134 135 if (stringformat.indexOf(HYPHEN) != -1) { 136 tk = new StringTokenizer(stringformat,HYPHEN); 137 } else { 138 tk = new StringTokenizer(stringformat,UNDERSCORE); 139 } 140 141 if (tk != null) { 142 if (tk.hasMoreTokens()) { 143 lang = tk.nextToken(); 144 } 145 if (tk.hasMoreTokens()) { 146 country = tk.nextToken(); 147 } 148 if (tk.hasMoreTokens()) { 149 variant = tk.nextToken(); 150 } 151 locale = new java.util.Locale(lang, country, variant); 152 } 153 154 return locale; 155 } 156 157 /** 158 * Returns locale from accept-language header HTTP accept language header 159 * can have more than one language in the header, we honor the first 160 * language as locale 161 * 162 * @param langstr 163 * Value from Accept-Language header of HTTP 164 * @return locale string in this format <code>en_US, fr</code> 165 */ 166 public static String getLocaleStringFromAcceptLangHeader(String langstr) { 167 168 if (langstr == null) 169 return null; 170 171 char[] lstr = langstr.toCharArray(); 172 int leadSpace = 0; 173 /* 174 * Accept Language Syntax Accept-Language = "Accept-Language" ":" 1#( 175 * language-range [ ";" "q" "=" qvalue ] ) language-range = ( ( 1*8ALPHA 176 * *("-" 1*8ALPHA ) ) | "*" ) For more info Read RFC 2616 Examples: 177 * Accept-Language: da, en-gb;q=0.8, en;q=0.7 Accept-Language: en-gb, en 178 * Accept-Language: ja Accept-Language: zh-cn Accept-Language: * 179 * 180 * We will use first language as locale. We will not process any 181 * further.Netscape,IE will give mostly one language as Accept-Language 182 * header. Max length of string is 5 lang-> 2chars , country -> two 183 * chars and separator is - 184 */ 185 186 try { 187 while (Character.isWhitespace(lstr[leadSpace])) 188 leadSpace++; 189 int len = lstr.length; 190 if (len > leadSpace + LOCALE_STRING_MAX_LEN) 191 len = leadSpace + LOCALE_STRING_MAX_LEN; 192 193 boolean isCountry = false; 194 for (int i = leadSpace; i < len; i++) { 195 char ch = lstr[i]; 196 if (ch == '*') 197 return null; 198 // "*" can be a valid accept-lang but does 199 // give idea about locale, return null and force the caller to 200 // use 201 // default locale 202 if (ch == '-') { 203 lstr[i] = '_'; // We will follow Java mechanism en_US 204 isCountry = true; 205 } else if (ch == ';' || ch == ',') {// Language separators used 206 // by accept-lang 207 return new String(lstr, leadSpace, i - leadSpace); 208 } else if (isCountry) { 209 lstr[i] = Character.toUpperCase(ch); 210 } 211 } 212 return new String(lstr, 0, len); 213 } catch (IndexOutOfBoundsException ex) { 214 return null; 215 } 216 } 217 218 /** 219 * Gets locale from accept-language header HTTP accept language header can 220 * have more than one language in the header, we honor the first language as 221 * locale 222 * 223 * @param langStr 224 * Value from Accept-Language header of HTTP 225 * @return locale string in this format <code>en_US, fr</code>. 226 */ 227 public static java.util.Locale getLocaleObjFromAcceptLangHeader( 228 String langStr) { 229 String lstr = getLocaleStringFromAcceptLangHeader(langStr); 230 231 if (lstr == null) 232 return null; 233 String lang = lstr.substring(0, 2); 234 String country = ""; 235 if (lstr.length() == LOCALE_STRING_MAX_LEN) 236 country = lstr.substring(3, 5); 237 return new java.util.Locale(lang, country); 238 } 239 240 /** 241 * Gets the resource bundle corresponding to the specified locale and the 242 * localized property file name. 243 * 244 * @param bundle 245 * Localized property file name. 246 * @param stringformat 247 * String representation of the locale. 248 * 249 * @return <code>java.util.ResourceBundle</code> object. 250 * 251 */ 252 public static ResourceBundle getResourceBundle(String bundle, 253 String stringformat) { 254 return ResourceBundle.getBundle(bundle, getLocale(stringformat)); 255 } 256 257 protected static ResourceBundle getResourceBundle(String bundle) { 258 return getInstallResourceBundle(bundle); 259 } 260 261 /** 262 * Gets the default install resource bundle for the default locale 263 * 264 * @param bundle 265 * Localized property file name 266 * @return the install resource bundle object 267 */ 268 public static ResourceBundle getInstallResourceBundle(String bundle) { 269 String loc = SystemPropertiesManager.get(Constants.AM_LOCALE, "en_US"); 270 return ResourceBundle.getBundle(bundle, getLocale(loc)); 271 } 272 273 /** 274 * Gets the default locale 275 * 276 * @return the default Locale object 277 */ 278 public static java.util.Locale getDefaultLocale() { 279 String loc = SystemPropertiesManager.get(Constants.AM_LOCALE, "en_US"); 280 return getLocale(loc); 281 } 282 283 /** 284 * Formats messages using <code>MessageFormat</code> Class. 285 * 286 * @param formatStr 287 * string format template. 288 * @param obj1 289 * object to be added to the template. 290 * @return formatted message. 291 */ 292 public static String formatMessage(String formatStr, Object obj1) { 293 Object arr[] = new Object[1]; 294 arr[0] = obj1; 295 return MessageFormat.format(formatStr, arr); 296 } 297 298 /** 299 * Formats to format messages using <code>MessageFormat</code> Class. 300 * given params to format them with 301 * 302 * @param formatStr 303 * string format template. 304 * @param objs 305 * objects to be added to the template. 306 * @return formatted message. 307 */ 308 public static String formatMessage(String formatStr, Object[] objs) { 309 return MessageFormat.format(formatStr, objs); 310 } 311 312 /** 313 * Returns the Date object from the date string in <code>ISO-8601</code> 314 * format. OpenAM stores date in <code>ISO-8601</code> format 315 * <code>yyyy/MM/yy hh:mm</code> 316 * 317 * @param dateString 318 * in the format <code>2002/12/31 23:59</code>. 319 * @return Date object 320 */ 321 public static Date parseNormalizedDateString(String dateString) { 322 if (dateString == null) 323 return null; 324 325 ParsePosition pos = new ParsePosition(0); 326 Date date = normalizedDateFormat.parse(dateString, pos); 327 if (date == null) { 328 debug.error("Locale.parseNormalizedDateString: " 329 + "Unable to parse date string"); 330 } 331 if (debug.messageEnabled()) { 332 debug.message("Locale.parseNormalizedDateString(" + dateString 333 + ")=" + date); 334 } 335 return date; 336 337 } 338 339 /** 340 * Gets Date object from date string with specified locale. 341 * 342 * @param dateString 343 * date string 344 * @param locale 345 * Locale object 346 * @param dateSyntax 347 * syntax of the date string. 348 * 349 * @return Date object returned if <code>dateString</code> matches the 350 * <code> dateSyntax</code>. If the syntax or date string is 351 * empty, or the string does not match the syntax, null will be 352 * returned. 353 */ 354 public static Date parseDateString(String dateString, 355 java.util.Locale locale, String dateSyntax) { 356 if (debug.messageEnabled()) { 357 debug.message("Local.parseDateString(date, locale, syntax)"); 358 debug.message("date string = " + dateString); 359 debug.message("date syntax = " + dateSyntax); 360 debug.message("locale = " + locale.toString()); 361 } 362 if ((dateString == null) || (dateString.length() < 1) 363 || (dateSyntax == null) || (dateSyntax.length() < 1)) { 364 return null; 365 } 366 367 SimpleDateFormat sdf = new SimpleDateFormat(dateSyntax); 368 sdf.setLenient(false); 369 ParsePosition pos = new ParsePosition(0); 370 Date date = sdf.parse(dateString, pos); 371 if (date == null) { 372 debug.warning("Locale.parseDateString: unable to parse the date."); 373 } 374 375 return date; 376 } 377 378 /** 379 * Gets Date object from date string with specified locale. Syntax of date 380 * string is defined in amUser_<locale> properties file. 381 * 382 * @param dateString 383 * date string 384 * @param locale 385 * Locale object 386 * 387 * @return Date object. null will be returned if error happens 388 */ 389 public static Date parseDateString(String dateString, 390 java.util.Locale locale) { 391 ResourceBundle rb = AMResourceBundleCache.getInstance().getResBundle( 392 USER_PROPERTIES, locale); 393 394 if (rb == null) { 395 debug.error("Locale.parseDateString: Unable to get resource " 396 + "bundle. Locale = " + locale); 397 return null; 398 } 399 400 String dateSyntax = null; 401 try { 402 dateSyntax = rb.getString(DATE_SYNTAX); 403 dateSyntax = dateSyntax.trim(); 404 } catch (Exception ex) { 405 debug.error("Locale.parseDateString: Unable to get " + DATE_SYNTAX 406 + ". Locale " + locale); 407 return null; 408 } 409 return parseDateString(dateString, locale, dateSyntax); 410 } 411 412 /** 413 * Converts the Date object into <code>ISO-8601</code> format 414 * <code>yyyy/MM/dd HH:mm</code> like <code>2002/12/23 20:40</code>. 415 * 416 * @param date 417 * to be normalized. 418 * @return date in <code>ISO8601</code> format 419 * <code>2002/12/31 11:59</code>. 420 */ 421 public static String getNormalizedDateString(Date date) { 422 if (date == null) 423 return null; 424 return normalizedDateFormat.format(date); 425 } 426 427 /** 428 * Gets date string from date with specified locale. 429 * 430 * @param date 431 * Date object 432 * @param locale 433 * Locale object 434 * 435 * @return date string. null will be returned if error happens 436 */ 437 public static String getDateString(Date date, java.util.Locale locale) { 438 if (date == null) { 439 return null; 440 } 441 442 ResourceBundle rb = AMResourceBundleCache.getInstance().getResBundle( 443 USER_PROPERTIES, locale); 444 if (rb == null) { 445 debug.error("Locale.getDateString: Unable to get resource " 446 + "bundle. Locale = " + locale); 447 return null; 448 } 449 450 String dateSyntax = null; 451 try { 452 dateSyntax = rb.getString(DATE_SYNTAX); 453 } catch (Exception ex) { 454 debug.error("Locale.getDateString: Unable to get " + DATE_SYNTAX 455 + ". Locale " + locale); 456 return null; 457 } 458 459 if (debug.messageEnabled()) { 460 debug.message("Locale.getDateString: dateSyntax = " + dateSyntax); 461 } 462 463 SimpleDateFormat sdf = new SimpleDateFormat(dateSyntax); 464 return sdf.format(date); 465 } 466 467 /** 468 * Converts date string from source locale to destination locale 469 * 470 * @param srcDateString 471 * source date string 472 * @param srcLocale 473 * source Locale object 474 * @param dstLocale 475 * destination Locale object 476 * 477 * @return converted date string. null will be returned if error happens 478 */ 479 public static String convertDateString(String srcDateString, 480 java.util.Locale srcLocale, java.util.Locale dstLocale) { 481 Date date = parseDateString(srcDateString, srcLocale); 482 483 return getDateString(date, dstLocale); 484 } 485 486 /** 487 * Gets the localized string for the specified key formatted as per passed 488 * parameters. 489 * 490 * @param rb 491 * resource bundle. 492 * @param resource 493 * the specified key. 494 * @param params 495 * formatting done as per these parameters. 496 * 497 * @return the localized string representation formatted as per passed 498 * parameters. 499 */ 500 public static String getString(ResourceBundle rb, String resource, 501 Object[] params) { 502 try { 503 return MessageFormat.format(rb.getString(resource), params); 504 } catch (Exception mre) { 505 if (debug.messageEnabled()) { 506 debug.message("missing resource: " + resource); 507 } 508 } 509 return resource; 510 } 511 512 /** 513 * Gets the localized string for the specified key from the specified 514 * Resource or from the specified default resource formatted as per provided 515 * parameters. 516 * 517 * @param rb 518 * resource bundle. 519 * @param resource 520 * the specified key. 521 * @param defaultRb 522 * Default resource bundle. 523 * @param params 524 * formatting done as per these parameters. 525 * 526 * @return the localized string representation formatted as per passed 527 * parameters. 528 * 529 */ 530 public static String getString(ResourceBundle rb, String resource, 531 ResourceBundle defaultRb, Object[] params) { 532 try { 533 return MessageFormat.format(rb.getString(resource), params); 534 } catch (Exception mre) { 535 try { 536 if (debug.messageEnabled()) { 537 debug.message("missing resource: " + resource); 538 debug.message("fall back to default resource bundle"); 539 } 540 return MessageFormat.format(defaultRb.getString(resource), 541 params); 542 } catch (Exception mrde) { 543 if (debug.messageEnabled()) { 544 debug.message("missing resource in default resource bundle:" 545 + resource); 546 } 547 } 548 } 549 return resource; 550 } 551 552 /** 553 * Gets the localized string for the specified key 554 * 555 * @param rb 556 * resource bundle. 557 * @param resource 558 * the specified key. 559 * @param debug 560 * the debug instance to which the debug messages need to be 561 * printed. 562 * 563 * @return the localized string representation 564 */ 565 public static String getString(ResourceBundle rb, String resource, 566 Debug debug) { 567 try { 568 return rb.getString(resource); 569 } catch (Exception mre) { 570 if (debug.messageEnabled()) { 571 debug.message("missing resource: " + resource); 572 } 573 } 574 return resource; 575 } 576 577 /** 578 * Gets the localized string for the specified key from the specified 579 * Resource or from the specified default resource 580 * 581 * @param rb 582 * resource bundle. 583 * @param resource 584 * the specified key. 585 * @param debug 586 * the debug instance to which the debug messages need to be 587 * printed. 588 * @param defaultRb 589 * Default resource bundle. 590 * 591 * @return the localized string representation 592 */ 593 public static String getString(ResourceBundle rb, String resource, 594 Debug debug, ResourceBundle defaultRb) { 595 try { 596 return rb.getString(resource); 597 } catch (Exception mre) { 598 try { 599 if (debug.messageEnabled()) { 600 debug.message("missing resource: " + resource); 601 debug.message("fall back to default resource bundle"); 602 } 603 return defaultRb.getString(resource); 604 } catch (Exception mrde) { 605 if (debug.messageEnabled()) { 606 debug.message("missing resource in default resource bundle:" 607 + resource); 608 } 609 } 610 } 611 return resource; 612 } 613 614 /** 615 * Gets the localized string for the specified key. 616 * 617 * @param rb 618 * resource bundle. 619 * @param resource 620 * the specified key. 621 * @return the localized string representation 622 */ 623 public static String getString(ResourceBundle rb, String resource) { 624 try { 625 return rb.getString(resource); 626 } catch (Exception mre) { 627 if (debug.messageEnabled()) { 628 debug.message("missing resource: " + resource); 629 } 630 } 631 return resource; 632 } 633 634 /** 635 * Gets the localized string for the specified key from the specified 636 * Resource or from the specified default resource. 637 * 638 * @param rb 639 * resource bundle. 640 * @param resource 641 * the specified key. 642 * @param defaultRb 643 * Default resource bundle. 644 * @return the localized string representation 645 */ 646 public static String getString(ResourceBundle rb, String resource, 647 ResourceBundle defaultRb) { 648 try { 649 return rb.getString(resource); 650 } catch (Exception mre) { 651 try { 652 if (debug.messageEnabled()) { 653 debug.message("missing resource: " + resource); 654 debug.message("fall back to default resource bundle"); 655 } 656 return defaultRb.getString(resource); 657 } catch (Exception mrde) { 658 if (debug.messageEnabled()) { 659 debug.message("missing resource in default resource bundle:" 660 + resource); 661 } 662 } 663 } 664 return resource; 665 } 666 667 /** 668 * This method is replacement function for <code>URLEncoder</code> 669 * Function URL encoder function converts input string into 670 * <code>URLEncoded</code> byte stream after converting Unicode string 671 * into bytes using native encoding. The <code>URLEncoder</code> does not 672 * work for OpenAM if default encoding is not 673 * <code>UTF-8</code>, hence this method was written. 674 * 675 * @param input 676 * the input string. 677 * @param enc 678 * the encoding format. 679 * @return the encoded string. 680 * @throws UnsupportedEncodingException 681 */ 682 public static String URLEncodeField(String input, String enc) 683 throws UnsupportedEncodingException { 684 int inputLen = input.length(); 685 686 byte[] byteOut = input.getBytes(enc); 687 StringBuffer result = new StringBuffer(inputLen * 4); // approx size 688 for (int i = 0; i < byteOut.length; i++) { 689 int c = byteOut[i] & 0xff; 690 if (dontEncode.get(c)) { 691 if (c == ' ') { 692 c = '+'; 693 } 694 result.append((char) c); 695 } else { 696 result.append('%'); 697 char ch = Character.forDigit((c >> 4) & 0xF, 16); 698 if (('a' <= ch) && (ch <= 'f')) { 699 ch -= caseDiff; 700 } 701 result.append(ch); 702 ch = Character.forDigit(c & 0xF, 16); 703 if (('a' <= ch) && (ch <= 'f')) { 704 ch -= caseDiff; 705 } 706 result.append(ch); 707 } 708 709 } 710 return result.toString(); 711 } 712 713 /** 714 * This method is replacement function for <code>URLEncoder<code> Function 715 * URL encoder function converts input string into <code>URLencoded</code> 716 * byte stream after converting Unicode string into bytes using native 717 * encoding. The <code>URLEncoder</code> does not work for Sun Java System 718 * OpenAM if default encoding is not <code>UTF-8</code>, hence this 719 * method was written. 720 * 721 * @param input the input string 722 * @param enc the encoding format 723 * @param debug the debug instance to which debug messages need to 724 * be printed 725 * 726 * @return the encoded string 727 */ 728 public static String URLEncodeField(String input, String enc, Debug debug) { 729 int inputLen = input.length(); 730 731 byte[] byteOut; 732 try { 733 byteOut = input.getBytes(enc); 734 } catch (UnsupportedEncodingException ex) { 735 if (debug != null) { 736 debug.error("Locale.URLEncodeField: Unsupported Encoding " 737 + enc, ex); 738 } 739 return input; 740 } 741 742 StringBuffer result = new StringBuffer(inputLen * 4); // approx size 743 for (int i = 0; i < byteOut.length; i++) { 744 int c = byteOut[i] & 0xff; 745 if (dontEncode.get(c)) { 746 if (c == ' ') { 747 c = '+'; 748 } 749 result.append((char) c); 750 } else { 751 result.append('%'); 752 char ch = Character.forDigit((c >> 4) & 0xF, 16); 753 if (('a' <= ch) && (ch <= 'f')) { 754 ch -= caseDiff; 755 } 756 result.append(ch); 757 ch = Character.forDigit(c & 0xF, 16); 758 if (('a' <= ch) && (ch <= 'f')) { 759 ch -= caseDiff; 760 } 761 result.append(ch); 762 } 763 764 } 765 return result.toString(); 766 } 767 768 static public String URLDecodeField(String strIn, Debug debug) { 769 return URLDecodeField(strIn, "UTF-8", debug); 770 } 771 772 /* 773 * Translate the individual field values in the encoding value Do not use 774 * getBytes instead convert unicode into bytes by casting. Using getBytes 775 * results in conversion into platform encoding. It appears to work file in 776 * C locale because default encoding is 8859-1 but fails in japanese locale. 777 * 778 * @param strIn the inputString @param charset character encoding of 779 * inputString @param debug the debug instance to which debug messages need 780 * to be printed. 781 * 782 * @return the decoded string 783 */ 784 static public String URLDecodeField(String strIn, String charset, 785 Debug debug) { 786 787 if (strIn == null) { 788 return strIn; 789 } 790 String strOut = null; 791 try { 792 int len = strIn.length(); 793 byte buf[] = new byte[len]; 794 795 int i = 0; 796 int offset = 0; 797 char[] carr = strIn.toCharArray(); 798 while (i < len) { 799 byte b = (byte) carr[i]; 800 switch (b) { 801 case '%': 802 int val = 0; 803 if (i + 2 < len) { 804 i++; 805 b = (byte) carr[i]; 806 if ('a' <= b && b <= 'f') { 807 b -= caseDiff; 808 } 809 if ('A' <= b && b <= 'F') { 810 val = 10 + b - 'A'; 811 val = val << 4; 812 } else if ('0' <= b && b <= '9') { 813 val = (b - '0') << 4; 814 } else { 815 throw new IllegalArgumentException( 816 "invalid hex char"); 817 } 818 i++; 819 b = (byte) carr[i]; 820 if ('a' <= b && b <= 'f') { 821 b -= caseDiff; 822 } 823 if ('A' <= b && b <= 'F') { 824 val += 10 + b - 'A'; 825 } else if ('0' <= b && b <= '9') { 826 val += b - '0'; 827 } else { 828 throw new IllegalArgumentException( 829 "invalid hex char"); 830 } 831 buf[offset++] = (byte) val; 832 i++; 833 } else { 834 buf[offset++] = (byte) carr[i++]; 835 } 836 break; 837 default: 838 buf[offset++] = (byte) carr[i++]; 839 break; 840 } 841 } 842 if (charset == null || charset.length() == 0) { 843 strOut = new String(buf, 0, offset, "UTF-8"); 844 } else { 845 strOut = new String(buf, 0, offset, charset); 846 } 847 } catch (Exception ex) { 848 debug.error("Locale::decodeField", ex); 849 strOut = strIn; 850 } 851 return strOut; 852 } 853}