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 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        return defaultLocale;
280    }
281
282    /**
283     * Formats messages using <code>MessageFormat</code> Class.
284     * 
285     * @param formatStr
286     *            string format template.
287     * @param obj1
288     *            object to be added to the template.
289     * @return formatted message.
290     */
291    public static String formatMessage(String formatStr, Object obj1) {
292        Object arr[] = new Object[1];
293        arr[0] = obj1;
294        return MessageFormat.format(formatStr, arr);
295    }
296
297    /**
298     * Formats to format messages using <code>MessageFormat</code> Class.
299     * given params to format them with
300     * 
301     * @param formatStr
302     *            string format template.
303     * @param objs
304     *            objects to be added to the template.
305     * @return formatted message.
306     */
307    public static String formatMessage(String formatStr, Object[] objs) {
308        return MessageFormat.format(formatStr, objs);
309    }
310
311    /**
312     * Returns the Date object from the date string in <code>ISO-8601</code>
313     * format. OpenSSO stores date in <code>ISO-8601</code> format
314     * <code>yyyy/MM/yy hh:mm</code>
315     * 
316     * @param dateString
317     *            in the format <code>2002/12/31 23:59</code>.
318     * @return Date object
319     */
320    public static Date parseNormalizedDateString(String dateString) {
321        if (dateString == null)
322            return null;
323
324        ParsePosition pos = new ParsePosition(0);
325        Date date = normalizedDateFormat.parse(dateString, pos);
326        if (date == null) {
327            debug.error("Locale.parseNormalizedDateString: "
328                    + "Unable to parse date string");
329        }
330        if (debug.messageEnabled()) {
331            debug.message("Locale.parseNormalizedDateString(" + dateString
332                    + ")=" + date);
333        }
334        return date;
335
336    }
337
338    /**
339     * Gets Date object from date string with specified locale.
340     * 
341     * @param dateString
342     *            date string
343     * @param locale
344     *            Locale object
345     * @param dateSyntax
346     *            syntax of the date string.
347     * 
348     * @return Date object returned if <code>dateString</code> matches the
349     *         <code> dateSyntax</code>. If the syntax or date string is
350     *         empty, or the string does not match the syntax, null will be
351     *         returned.
352     */
353    public static Date parseDateString(String dateString,
354            java.util.Locale locale, String dateSyntax) {
355        if (debug.messageEnabled()) {
356            debug.message("Local.parseDateString(date, locale, syntax)");
357            debug.message("date string = " + dateString);
358            debug.message("date syntax = " + dateSyntax);
359            debug.message("locale = " + locale.toString());
360        }
361        if ((dateString == null) || (dateString.length() < 1)
362                || (dateSyntax == null) || (dateSyntax.length() < 1)) {
363            return null;
364        }
365
366        SimpleDateFormat sdf = new SimpleDateFormat(dateSyntax);
367        sdf.setLenient(false);
368        ParsePosition pos = new ParsePosition(0);
369        Date date = sdf.parse(dateString, pos);
370        if (date == null) {
371            debug.warning("Locale.parseDateString: unable to parse the date.");
372        }
373
374        return date;
375    }
376
377    /**
378     * Gets Date object from date string with specified locale. Syntax of date
379     * string is defined in amUser_&lt;locale> properties file.
380     * 
381     * @param dateString
382     *            date string
383     * @param locale
384     *            Locale object
385     * 
386     * @return Date object. null will be returned if error happens
387     */
388    public static Date parseDateString(String dateString,
389            java.util.Locale locale) {
390        ResourceBundle rb = AMResourceBundleCache.getInstance().getResBundle(
391                USER_PROPERTIES, locale);
392
393        if (rb == null) {
394            debug.error("Locale.parseDateString: Unable to get resource "
395                    + "bundle. Locale = " + locale);
396            return null;
397        }
398
399        String dateSyntax = null;
400        try {
401            dateSyntax = rb.getString(DATE_SYNTAX);
402            dateSyntax = dateSyntax.trim();
403        } catch (Exception ex) {
404            debug.error("Locale.parseDateString: Unable to get " + DATE_SYNTAX
405                    + ". Locale " + locale);
406            return null;
407        }
408        return parseDateString(dateString, locale, dateSyntax);
409    }
410
411    /**
412     * Converts the Date object into <code>ISO-8601</code> format
413     * <code>yyyy/MM/dd HH:mm</code> like <code>2002/12/23 20:40</code>.
414     * 
415     * @param date
416     *            to be normalized.
417     * @return date in <code>ISO8601</code> format
418     *         <code>2002/12/31 11:59</code>.
419     */
420    public static String getNormalizedDateString(Date date) {
421        if (date == null)
422            return null;
423        return normalizedDateFormat.format(date);
424    }
425
426    /**
427     * Gets date string from date with specified locale.
428     * 
429     * @param date
430     *            Date object
431     * @param locale
432     *            Locale object
433     * 
434     * @return date string. null will be returned if error happens
435     */
436    public static String getDateString(Date date, java.util.Locale locale) {
437        if (date == null) {
438            return null;
439        }
440
441        ResourceBundle rb = AMResourceBundleCache.getInstance().getResBundle(
442                USER_PROPERTIES, locale);
443        if (rb == null) {
444            debug.error("Locale.getDateString: Unable to get resource "
445                    + "bundle. Locale = " + locale);
446            return null;
447        }
448
449        String dateSyntax = null;
450        try {
451            dateSyntax = rb.getString(DATE_SYNTAX);
452        } catch (Exception ex) {
453            debug.error("Locale.getDateString: Unable to get " + DATE_SYNTAX
454                    + ". Locale " + locale);
455            return null;
456        }
457
458        if (debug.messageEnabled()) {
459            debug.message("Locale.getDateString: dateSyntax = " + dateSyntax);
460        }
461
462        SimpleDateFormat sdf = new SimpleDateFormat(dateSyntax);
463        return sdf.format(date);
464    }
465
466    /**
467     * Converts date string from source locale to destination locale
468     * 
469     * @param srcDateString
470     *            source date string
471     * @param srcLocale
472     *            source Locale object
473     * @param dstLocale
474     *            destination Locale object
475     * 
476     * @return converted date string. null will be returned if error happens
477     */
478    public static String convertDateString(String srcDateString,
479            java.util.Locale srcLocale, java.util.Locale dstLocale) {
480        Date date = parseDateString(srcDateString, srcLocale);
481
482        return getDateString(date, dstLocale);
483    }
484
485    /**
486     * Gets the localized string for the specified key formatted as per passed
487     * parameters.
488     * 
489     * @param rb
490     *            resource bundle.
491     * @param resource
492     *            the specified key.
493     * @param params
494     *            formatting done as per these parameters.
495     * 
496     * @return the localized string representation formatted as per passed
497     *         parameters.
498     */
499    public static String getString(ResourceBundle rb, String resource,
500            Object[] params) {
501        try {
502            return MessageFormat.format(rb.getString(resource), params);
503        } catch (Exception mre) {
504            if (debug.messageEnabled()) {
505                debug.message("missing resource: " + resource);
506            }
507        }
508        return resource;
509    }
510
511    /**
512     * Gets the localized string for the specified key from the specified
513     * Resource or from the specified default resource formatted as per provided
514     * parameters.
515     * 
516     * @param rb
517     *            resource bundle.
518     * @param resource
519     *            the specified key.
520     * @param defaultRb
521     *            Default resource bundle.
522     * @param params
523     *            formatting done as per these parameters.
524     * 
525     * @return the localized string representation formatted as per passed
526     *         parameters.
527     * 
528     */
529    public static String getString(ResourceBundle rb, String resource,
530            ResourceBundle defaultRb, Object[] params) {
531        try {
532            return MessageFormat.format(rb.getString(resource), params);
533        } catch (Exception mre) {
534            try {
535                if (debug.messageEnabled()) {
536                    debug.message("missing resource: " + resource);
537                    debug.message("fall back to default resource bundle");
538                }
539                return MessageFormat.format(defaultRb.getString(resource),
540                        params);
541            } catch (Exception mrde) {
542                if (debug.messageEnabled()) {
543                    debug.message("missing resource in default resource bundle:"
544                                    + resource);
545                }
546            }
547        }
548        return resource;
549    }
550
551    /**
552     * Gets the localized string for the specified key
553     * 
554     * @param rb
555     *            resource bundle.
556     * @param resource
557     *            the specified key.
558     * @param debug
559     *            the debug instance to which the debug messages need to be
560     *            printed.
561     * 
562     * @return the localized string representation
563     */
564    public static String getString(ResourceBundle rb, String resource,
565            Debug debug) {
566        try {
567            return rb.getString(resource);
568        } catch (Exception mre) {
569            if (debug.messageEnabled()) {
570                debug.message("missing resource: " + resource);
571            }
572        }
573        return resource;
574    }
575
576    /**
577     * Gets the localized string for the specified key from the specified
578     * Resource or from the specified default resource
579     * 
580     * @param rb
581     *            resource bundle.
582     * @param resource
583     *            the specified key.
584     * @param debug
585     *            the debug instance to which the debug messages need to be
586     *            printed.
587     * @param defaultRb
588     *            Default resource bundle.
589     * 
590     * @return the localized string representation
591     */
592    public static String getString(ResourceBundle rb, String resource,
593            Debug debug, ResourceBundle defaultRb) {
594        try {
595            return rb.getString(resource);
596        } catch (Exception mre) {
597            try {
598                if (debug.messageEnabled()) {
599                    debug.message("missing resource: " + resource);
600                    debug.message("fall back to default resource bundle");
601                }
602                return defaultRb.getString(resource);
603            } catch (Exception mrde) {
604                if (debug.messageEnabled()) {
605                    debug.message("missing resource in default resource bundle:"
606                                    + resource);
607                }
608            }
609        }
610        return resource;
611    }
612
613    /**
614     * Gets the localized string for the specified key.
615     * 
616     * @param rb
617     *            resource bundle.
618     * @param resource
619     *            the specified key.
620     * @return the localized string representation
621     */
622    public static String getString(ResourceBundle rb, String resource) {
623        try {
624            return rb.getString(resource);
625        } catch (Exception mre) {
626            if (debug.messageEnabled()) {
627                debug.message("missing resource: " + resource);
628            }
629        }
630        return resource;
631    }
632
633    /**
634     * Gets the localized string for the specified key from the specified
635     * Resource or from the specified default resource.
636     * 
637     * @param rb
638     *            resource bundle.
639     * @param resource
640     *            the specified key.
641     * @param defaultRb
642     *            Default resource bundle.
643     * @return the localized string representation
644     */
645    public static String getString(ResourceBundle rb, String resource,
646            ResourceBundle defaultRb) {
647        try {
648            return rb.getString(resource);
649        } catch (Exception mre) {
650            try {
651                if (debug.messageEnabled()) {
652                    debug.message("missing resource: " + resource);
653                    debug.message("fall back to default resource bundle");
654                }
655                return defaultRb.getString(resource);
656            } catch (Exception mrde) {
657                if (debug.messageEnabled()) {
658                    debug.message("missing resource in default resource bundle:"
659                                    + resource);
660                }
661            }
662        }
663        return resource;
664    }
665
666    /**
667     * This method is replacement function for <code>URLEncoder</code>
668     * Function URL encoder function converts input string into
669     * <code>URLEncoded</code> byte stream after converting Unicode string
670     * into bytes using native encoding. The <code>URLEncoder</code> does not
671     * work for OpenSSO if default encoding is not
672     * <code>UTF-8</code>, hence this method was written.
673     * 
674     * @param input
675     *            the input string.
676     * @param enc
677     *            the encoding format.
678     * @return the encoded string.
679     * @throws UnsupportedEncodingException
680     */
681    public static String URLEncodeField(String input, String enc)
682            throws UnsupportedEncodingException {
683        int inputLen = input.length();
684
685        byte[] byteOut = input.getBytes(enc);
686        StringBuffer result = new StringBuffer(inputLen * 4); // approx size
687        for (int i = 0; i < byteOut.length; i++) {
688            int c = byteOut[i] & 0xff;
689            if (dontEncode.get(c)) {
690                if (c == ' ') {
691                    c = '+';
692                }
693                result.append((char) c);
694            } else {
695                result.append('%');
696                char ch = Character.forDigit((c >> 4) & 0xF, 16);
697                if (('a' <= ch) && (ch <= 'f')) {
698                    ch -= caseDiff;
699                }
700                result.append(ch);
701                ch = Character.forDigit(c & 0xF, 16);
702                if (('a' <= ch) && (ch <= 'f')) {
703                    ch -= caseDiff;
704                }
705                result.append(ch);
706            }
707
708        }
709        return result.toString();
710    }
711
712    /**
713     * This method is replacement function for <code>URLEncoder<code> Function
714     * URL encoder function converts input string into <code>URLencoded</code>
715     * byte stream after converting Unicode string into bytes using native
716     * encoding. The <code>URLEncoder</code> does not work for Sun Java System
717     * OpenSSO if default encoding is not <code>UTF-8</code>, hence this
718     * method was written.
719     * 
720     * @param input the input string
721     * @param enc the encoding format 
722     * @param debug the debug instance to which debug messages need to
723     * be printed
724     *
725     * @return the encoded string
726     */
727    public static String URLEncodeField(String input, String enc, Debug debug) {
728        int inputLen = input.length();
729
730        byte[] byteOut;
731        try {
732            byteOut = input.getBytes(enc);
733        } catch (UnsupportedEncodingException ex) {
734            if (debug != null) {
735                debug.error("Locale.URLEncodeField: Unsupported Encoding "
736                        + enc, ex);
737            }
738            return input;
739        }
740
741        StringBuffer result = new StringBuffer(inputLen * 4); // approx size
742        for (int i = 0; i < byteOut.length; i++) {
743            int c = byteOut[i] & 0xff;
744            if (dontEncode.get(c)) {
745                if (c == ' ') {
746                    c = '+';
747                }
748                result.append((char) c);
749            } else {
750                result.append('%');
751                char ch = Character.forDigit((c >> 4) & 0xF, 16);
752                if (('a' <= ch) && (ch <= 'f')) {
753                    ch -= caseDiff;
754                }
755                result.append(ch);
756                ch = Character.forDigit(c & 0xF, 16);
757                if (('a' <= ch) && (ch <= 'f')) {
758                    ch -= caseDiff;
759                }
760                result.append(ch);
761            }
762
763        }
764        return result.toString();
765    }
766
767    static public String URLDecodeField(String strIn, Debug debug) {
768        return URLDecodeField(strIn, "UTF-8", debug);
769    }
770
771    /*
772     * Translate the individual field values in the encoding value Do not use
773     * getBytes instead convert unicode into bytes by casting. Using getBytes
774     * results in conversion into platform encoding. It appears to work file in
775     * C locale because default encoding is 8859-1 but fails in japanese locale.
776     * 
777     * @param strIn the inputString @param charset character encoding of
778     * inputString @param debug the debug instance to which debug messages need
779     * to be printed.
780     * 
781     * @return the decoded string
782     */
783    static public String URLDecodeField(String strIn, String charset,
784            Debug debug) {
785
786        if (strIn == null) {
787            return strIn;
788        }
789        String strOut = null;
790        try {
791            int len = strIn.length();
792            byte buf[] = new byte[len];
793
794            int i = 0;
795            int offset = 0;
796            char[] carr = strIn.toCharArray();
797            while (i < len) {
798                byte b = (byte) carr[i];
799                switch (b) {
800                case '%':
801                    int val = 0;
802                    if (i + 2 < len) {
803                        i++;
804                        b = (byte) carr[i];
805                        if ('a' <= b && b <= 'f') {
806                            b -= caseDiff;
807                        }
808                        if ('A' <= b && b <= 'F') {
809                            val = 10 + b - 'A';
810                            val = val << 4;
811                        } else if ('0' <= b && b <= '9') {
812                            val = (b - '0') << 4;
813                        } else {
814                            throw new IllegalArgumentException(
815                                    "invalid hex char");
816                        }
817                        i++;
818                        b = (byte) carr[i];
819                        if ('a' <= b && b <= 'f') {
820                            b -= caseDiff;
821                        }
822                        if ('A' <= b && b <= 'F') {
823                            val += 10 + b - 'A';
824                        } else if ('0' <= b && b <= '9') {
825                            val += b - '0';
826                        } else {
827                            throw new IllegalArgumentException(
828                                    "invalid hex char");
829                        }
830                        buf[offset++] = (byte) val;
831                        i++;
832                    } else {
833                       buf[offset++] = (byte) carr[i++]; 
834                    }
835                    break;
836                default:
837                    buf[offset++] = (byte) carr[i++];
838                    break;
839                }
840            }
841            if (charset == null || charset.length() == 0) {
842                strOut = new String(buf, 0, offset, "UTF-8");
843            } else {
844                strOut = new String(buf, 0, offset, charset);
845            }
846        } catch (Exception ex) {
847            debug.error("Locale::decodeField", ex);
848            strOut = strIn;
849        }
850        return strOut;
851    }
852}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.