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