1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.util;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport dalvik.system.VMStack;
21565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.File;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
24565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.InputStreamReader;
25565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.net.URL;
26565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.net.URLConnection;
272a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughesimport java.nio.charset.StandardCharsets;
282c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilsonimport libcore.io.IoUtils;
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code ResourceBundle} is an abstract class which is the superclass of classes which
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * provide {@code Locale}-specific resources. A bundle contains a number of named
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * resources, where the names are {@code Strings}. A bundle may have a parent bundle,
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * and when a resource is not found in a bundle, the parent bundle is searched for
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the resource. If the fallback mechanism reaches the base bundle and still
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * can't find the resource it throws a {@code MissingResourceException}.
37f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <ul>
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>All bundles for the same group of resources share a common base bundle.
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * This base bundle acts as the root and is the last fallback in case none of
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * its children was able to respond to a request.</li>
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>The first level contains changes between different languages. Only the
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * differences between a language and the language of the base bundle need to be
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * handled by a language-specific {@code ResourceBundle}.</li>
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>The second level contains changes between different countries that use
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the same language. Only the differences between a country and the country of
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the language bundle need to be handled by a country-specific {@code ResourceBundle}.
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * </li>
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>The third level contains changes that don't have a geographic reason
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (e.g. changes that where made at some point in time like {@code PREEURO} where the
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * currency of come countries changed. The country bundle would return the
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * current currency (Euro) and the {@code PREEURO} variant bundle would return the old
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * currency (e.g. DM for Germany).</li>
54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * </ul>
55f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <strong>Examples</strong>
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <ul>
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName (base bundle)
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_de (german language bundle)
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_fr (french language bundle)
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_de_DE (bundle with Germany specific resources in german)
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_de_CH (bundle with Switzerland specific resources in german)
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_fr_CH (bundle with Switzerland specific resources in french)
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_de_DE_PREEURO (bundle with Germany specific resources in german of
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the time before the Euro)
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>BaseName_fr_FR_PREEURO (bundle with France specific resources in french of
67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the time before the Euro)
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * </ul>
69f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * It's also possible to create variants for languages or countries. This can be
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * done by just skipping the country or language abbreviation:
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * BaseName_us__POSIX or BaseName__DE_PREEURO. But it's not allowed to
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * circumvent both language and country: BaseName___VARIANT is illegal.
74f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see Properties
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see PropertyResourceBundle
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see ListResourceBundle
78f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * @since 1.1
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic abstract class ResourceBundle {
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
82f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private static final String UNDER_SCORE = "_";
83565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
84f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private static final String EMPTY_STRING = "";
85565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * The parent of this {@code ResourceBundle} that is used if this bundle doesn't
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * include the requested resource.
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected ResourceBundle parent;
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Locale locale;
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
94565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private long lastLoadTime = 0;
95565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    static class MissingBundle extends ResourceBundle {
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        @Override
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        public Enumeration<String> getKeys() {
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return null;
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        @Override
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        public Object handleGetObject(String name) {
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return null;
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final ResourceBundle MISSING = new MissingBundle();
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final ResourceBundle MISSINGBASE = new MissingBundle();
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1122c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    private static final WeakHashMap<Object, Hashtable<String, ResourceBundle>> cache
1132c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            = new WeakHashMap<Object, Hashtable<String, ResourceBundle>>();
1142c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson
1152c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    private static Locale cacheLocale = Locale.getDefault();
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new instance of this class.
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public ResourceBundle() {
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        /* empty */
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Finds the named resource bundle for the default {@code Locale} and the caller's
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code ClassLoader}.
127f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param bundleName
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the {@code ResourceBundle}.
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the requested {@code ResourceBundle}.
131f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the {@code ResourceBundle} cannot be found.
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1342c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static ResourceBundle getBundle(String bundleName) throws MissingResourceException {
1359ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        ClassLoader classLoader = VMStack.getCallingClassLoader();
1369ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        if (classLoader == null) {
1379ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson            classLoader = getLoader();
1389ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        }
1399ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        return getBundle(bundleName, Locale.getDefault(), classLoader);
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Finds the named {@code ResourceBundle} for the specified {@code Locale} and the caller
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code ClassLoader}.
145f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param bundleName
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the {@code ResourceBundle}.
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param locale
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code Locale}.
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the requested resource bundle.
151f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource bundle cannot be found.
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1542c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static ResourceBundle getBundle(String bundleName, Locale locale) {
1559ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        ClassLoader classLoader = VMStack.getCallingClassLoader();
1569ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        if (classLoader == null) {
1579ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson            classLoader = getLoader();
1589ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        }
1599ef23e5d7f45cf120abb0e140d657bf7c79b3721Jesse Wilson        return getBundle(bundleName, locale, classLoader);
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Finds the named resource bundle for the specified {@code Locale} and {@code ClassLoader}.
164f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * The passed base name and {@code Locale} are used to create resource bundle names.
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * The first name is created by concatenating the base name with the result
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * of {@link Locale#toString()}. From this name all parent bundle names are
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * derived. Then the same thing is done for the default {@code Locale}. This results
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * in a list of possible bundle names.
170f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <strong>Example</strong> For the basename "BaseName", the {@code Locale} of the
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * German part of Switzerland (de_CH) and the default {@code Locale} en_US the list
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * would look something like this:
174f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <ol>
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>BaseName_de_CH</li>
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>BaseName_de</li>
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>Basename_en_US</li>
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>Basename_en</li>
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>BaseName</li>
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * </ol>
182f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * This list also shows the order in which the bundles will be searched for a requested
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * resource in the German part of Switzerland (de_CH).
185f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
186f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * As a first step, this method tries to instantiate
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * a {@code ResourceBundle} with the names provided.
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * If such a class can be instantiated and initialized, it is returned and
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * all the parent bundles are instantiated too. If no such class can be
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found this method tries to load a {@code .properties} file with the names by
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * replacing dots in the base name with a slash and by appending
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * "{@code .properties}" at the end of the string. If such a resource can be found
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * by calling {@link ClassLoader#getResource(String)} it is used to
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * initialize a {@link PropertyResourceBundle}. If this succeeds, it will
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * also load the parents of this {@code ResourceBundle}.
196f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * For compatibility with older code, the bundle name isn't required to be
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * a fully qualified class name. It's also possible to directly pass
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * the path to a properties file (without a file extension).
200f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param bundleName
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the {@code ResourceBundle}.
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param locale
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code Locale}.
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param loader
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code ClassLoader} to use.
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the requested {@code ResourceBundle}.
208f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the {@code ResourceBundle} cannot be found.
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public static ResourceBundle getBundle(String bundleName, Locale locale,
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            ClassLoader loader) throws MissingResourceException {
21386acc043d3334651ee26c65467d78d6cefedd397Kenny Root        if (loader == null) {
21486acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("loader == null");
21586acc043d3334651ee26c65467d78d6cefedd397Kenny Root        } else if (bundleName == null) {
21686acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("bundleName == null");
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
2182c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Locale defaultLocale = Locale.getDefault();
2192c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (!cacheLocale.equals(defaultLocale)) {
2202c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            cache.clear();
2212c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            cacheLocale = defaultLocale;
2222c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        }
2232c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        ResourceBundle bundle = null;
2242c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (!locale.equals(defaultLocale)) {
2252c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            bundle = handleGetBundle(false, bundleName, locale, loader);
2262c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        }
2272c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (bundle == null) {
2282c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            bundle = handleGetBundle(true, bundleName, defaultLocale, loader);
2292c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            if (bundle == null) {
2302c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                throw missingResourceException(bundleName + '_' + locale, "");
231565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
232565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
2332c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        return bundle;
234565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
235565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
236565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private static MissingResourceException missingResourceException(String className, String key) {
237565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        String detail = "Can't find resource for bundle '" + className + "', key '" + key + "'";
238565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        throw new MissingResourceException(detail, className, key);
239565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
240565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
241565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
242565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Finds the named resource bundle for the specified base name and control.
243f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
244565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param baseName
245565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the base name of a resource bundle
246565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param control
247565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the control that control the access sequence
248565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @return the named resource bundle
249f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
250565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
251565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
2522c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static ResourceBundle getBundle(String baseName, ResourceBundle.Control control) {
253565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return getBundle(baseName, Locale.getDefault(), getLoader(), control);
254565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
255565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
256565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
257565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Finds the named resource bundle for the specified base name and control.
258f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
259565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param baseName
260565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the base name of a resource bundle
261565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param targetLocale
262565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the target locale of the resource bundle
263565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param control
264565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the control that control the access sequence
265565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @return the named resource bundle
266f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
267565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
268565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
2692c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static ResourceBundle getBundle(String baseName,
270565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            Locale targetLocale, ResourceBundle.Control control) {
271565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return getBundle(baseName, targetLocale, getLoader(), control);
272565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
273565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
274565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private static ClassLoader getLoader() {
275ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        ClassLoader cl = ResourceBundle.class.getClassLoader();
276ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        if (cl == null) {
277ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes            cl = ClassLoader.getSystemClassLoader();
278ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        }
279ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes        return cl;
280565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
281565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
282565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
283565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Finds the named resource bundle for the specified base name and control.
284f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
285565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param baseName
286565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the base name of a resource bundle
287565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param targetLocale
288565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the target locale of the resource bundle
289565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param loader
290565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the class loader to load resource
291565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param control
292565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     *            the control that control the access sequence
293565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @return the named resource bundle
294f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
295565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
296565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
297565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public static ResourceBundle getBundle(String baseName,
298565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            Locale targetLocale, ClassLoader loader,
299565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ResourceBundle.Control control) {
300565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        boolean expired = false;
301565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        String bundleName = control.toBundleName(baseName, targetLocale);
3022c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Object cacheKey = loader != null ? loader : "null";
3032c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Hashtable<String, ResourceBundle> loaderCache = getLoaderCache(cacheKey);
304565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        ResourceBundle result = loaderCache.get(bundleName);
305565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        if (result != null) {
306565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            long time = control.getTimeToLive(baseName, targetLocale);
307565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (time == 0 || time == Control.TTL_NO_EXPIRATION_CONTROL
308565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    || time + result.lastLoadTime < System.currentTimeMillis()) {
309565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (MISSING == result) {
310565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    throw new MissingResourceException(null, bundleName + '_'
311565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            + targetLocale, EMPTY_STRING);
312565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
313565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                return result;
314565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
315565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            expired = true;
316565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
317565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        // try to load
318565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        ResourceBundle ret = processGetBundle(baseName, targetLocale, loader,
319565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                control, expired, result);
320565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
321b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (ret != null) {
322565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            loaderCache.put(bundleName, ret);
323565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ret.lastLoadTime = System.currentTimeMillis();
324565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return ret;
325565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
326565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        loaderCache.put(bundleName, MISSING);
3272c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        throw new MissingResourceException(null, bundleName + '_' + targetLocale, EMPTY_STRING);
328565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
329565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
330565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private static ResourceBundle processGetBundle(String baseName,
331565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            Locale targetLocale, ClassLoader loader,
332565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ResourceBundle.Control control, boolean expired,
333565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ResourceBundle result) {
334b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        List<Locale> locales = control.getCandidateLocales(baseName, targetLocale);
335b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (locales == null) {
336565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException();
337565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
338565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        List<String> formats = control.getFormats(baseName);
339565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        if (Control.FORMAT_CLASS == formats
340565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                || Control.FORMAT_PROPERTIES == formats
341565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                || Control.FORMAT_DEFAULT == formats) {
342565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException();
343565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
344565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        ResourceBundle ret = null;
345565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        ResourceBundle currentBundle = null;
346565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        ResourceBundle bundle = null;
347565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        for (Locale locale : locales) {
348565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            for (String format : formats) {
349565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                try {
350565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    if (expired) {
351565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        bundle = control.newBundle(baseName, locale, format,
352565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                                loader, control.needsReload(baseName, locale,
353565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                                        format, loader, result, System
354565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                                                .currentTimeMillis()));
355565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
356565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    } else {
357565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        try {
358565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            bundle = control.newBundle(baseName, locale,
359565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                                    format, loader, false);
360565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        } catch (IllegalArgumentException e) {
361565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            // do nothing
362565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        }
363565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
364565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                } catch (IllegalAccessException e) {
365565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    // do nothing
366565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                } catch (InstantiationException e) {
367565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    // do nothing
368565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                } catch (IOException e) {
369565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    // do nothing
370565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
371b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                if (bundle != null) {
372b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                    if (currentBundle != null) {
373565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        currentBundle.setParent(bundle);
374565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        currentBundle = bundle;
375565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    } else {
376b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                        if (ret == null) {
377565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            ret = bundle;
378565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            currentBundle = ret;
379565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        }
380565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
381565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
382b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                if (bundle != null) {
383565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    break;
384565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
385565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
386565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
387565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
388b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if ((ret == null)
389565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                || (Locale.ROOT.equals(ret.getLocale()) && (!(locales.size() == 1 && locales
390565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        .contains(Locale.ROOT))))) {
391b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            Locale nextLocale = control.getFallbackLocale(baseName, targetLocale);
392b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (nextLocale != null) {
393565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret = processGetBundle(baseName, nextLocale, loader, control,
394565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        expired, result);
395565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
396565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
397565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
398565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return ret;
399adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
400adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
401adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
402adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the names of the resources contained in this {@code ResourceBundle}.
403f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
404adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return an {@code Enumeration} of the resource names.
405adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
406adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public abstract Enumeration<String> getKeys();
407adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
408adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
409adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Gets the {@code Locale} of this {@code ResourceBundle}. In case a bundle was not
410adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found for the requested {@code Locale}, this will return the actual {@code Locale} of
411adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * this resource bundle that was found after doing a fallback.
412f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
413adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the {@code Locale} of this {@code ResourceBundle}.
414adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
415adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Locale getLocale() {
416adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return locale;
417adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
418adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
419adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the named resource from this {@code ResourceBundle}. If the resource
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * cannot be found in this bundle, it falls back to the parent bundle (if
422adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * it's not null) by calling the {@link #handleGetObject} method. If the resource still
423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * can't be found it throws a {@code MissingResourceException}.
424f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
425adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param key
426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the resource.
427adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the resource object.
428f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
429adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource is not found.
430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
431adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public final Object getObject(String key) {
432adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ResourceBundle last, theParent = this;
433adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        do {
434adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Object result = theParent.handleGetObject(key);
435adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (result != null) {
436adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return result;
437adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            last = theParent;
439adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            theParent = theParent.parent;
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } while (theParent != null);
441565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        throw missingResourceException(last.getClass().getName(), key);
442adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
443adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
444adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
445adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the named string resource from this {@code ResourceBundle}.
446f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
447adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param key
448adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the resource.
449adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the resource string.
450f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
451adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource is not found.
452f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws ClassCastException
453adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource found is not a string.
454adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getObject(String)
455adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
456adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public final String getString(String key) {
457adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return (String) getObject(key);
458adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
459adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
460adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
461adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the named resource from this {@code ResourceBundle}.
462f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
463adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param key
464adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the resource.
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the resource string array.
466f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws MissingResourceException
467adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource is not found.
468f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws ClassCastException
469adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                if the resource found is not an array of strings.
470adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getObject(String)
471adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
472adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public final String[] getStringArray(String key) {
473adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return (String[]) getObject(key);
474adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
475adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
4762c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    private static ResourceBundle handleGetBundle(boolean loadBase, String base, Locale locale,
4772c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            ClassLoader loader) {
4782c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        String localeName = locale.toString();
4792c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        String bundleName = localeName.isEmpty()
4802c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                ? base
4812c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                : (base + "_" + localeName);
4822c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Object cacheKey = loader != null ? loader : "null";
4832c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Hashtable<String, ResourceBundle> loaderCache = getLoaderCache(cacheKey);
4842c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        ResourceBundle cached = loaderCache.get(bundleName);
4852c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (cached != null) {
4862c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            if (cached == MISSINGBASE) {
487adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return null;
4882c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            } else if (cached == MISSING) {
489adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (!loadBase) {
490adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return null;
491adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
4922c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                Locale newLocale = strip(locale);
4932c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                if (newLocale == null) {
494adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return null;
495adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
4962c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                return handleGetBundle(loadBase, base, newLocale, loader);
497adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
4982c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            return cached;
499adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
500adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5012c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        ResourceBundle bundle = null;
502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
503aba52f92911e0faa4ae1907becc5a66cdb3254deElliott Hughes            Class<?> bundleClass = Class.forName(bundleName, true, loader);
504aba52f92911e0faa4ae1907becc5a66cdb3254deElliott Hughes            if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
505aba52f92911e0faa4ae1907becc5a66cdb3254deElliott Hughes                bundle = (ResourceBundle) bundleClass.newInstance();
506adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
5072c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        } catch (LinkageError ignored) {
5082c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        } catch (Exception ignored) {
509adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
510adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
511adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (bundle != null) {
512adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            bundle.setLocale(locale);
513f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        } else {
5142c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            String fileName = bundleName.replace('.', '/') + ".properties";
5152c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            InputStream stream = loader != null
5162c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                    ? loader.getResourceAsStream(fileName)
5172c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                    : ClassLoader.getSystemResourceAsStream(fileName);
518adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (stream != null) {
519adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                try {
5202a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes                    bundle = new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
521adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    bundle.setLocale(locale);
5222c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                } catch (IOException ignored) {
5232c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                } finally {
5242c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                    IoUtils.closeQuietly(stream);
525adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
526adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
527adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
528adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5292c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        Locale strippedLocale = strip(locale);
530adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (bundle != null) {
5312c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            if (strippedLocale != null) {
5322c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                ResourceBundle parent = handleGetBundle(loadBase, base, strippedLocale, loader);
533adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (parent != null) {
534adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    bundle.setParent(parent);
535adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
536adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
537adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            loaderCache.put(bundleName, bundle);
538adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return bundle;
539adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
540adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5412c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (strippedLocale != null && (loadBase || !strippedLocale.toString().isEmpty())) {
5422c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            bundle = handleGetBundle(loadBase, base, strippedLocale, loader);
543adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (bundle != null) {
544adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                loaderCache.put(bundleName, bundle);
545adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return bundle;
546adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
547adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
548adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        loaderCache.put(bundleName, loadBase ? MISSINGBASE : MISSING);
549adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return null;
550adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
551adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5522c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    private static Hashtable<String, ResourceBundle> getLoaderCache(Object cacheKey) {
5532c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        synchronized (cache) {
5542c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            Hashtable<String, ResourceBundle> loaderCache = cache.get(cacheKey);
5552c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            if (loaderCache == null) {
5562c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                loaderCache = new Hashtable<String, ResourceBundle>();
5572c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson                cache.put(cacheKey, loaderCache);
5582c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            }
5592c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            return loaderCache;
5602c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        }
5612c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    }
5622c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson
563adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
564adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the named resource from this {@code ResourceBundle}, or null if the
565adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * resource is not found.
566f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
567adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param key
568adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the resource.
569adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the resource object.
570adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
571adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected abstract Object handleGetObject(String key);
572adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
573adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
574adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Sets the parent resource bundle of this {@code ResourceBundle}. The parent is
575adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * searched for resources which are not found in this {@code ResourceBundle}.
576f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
577adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param bundle
578adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the parent {@code ResourceBundle}.
579adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
580adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected void setParent(ResourceBundle bundle) {
581adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        parent = bundle;
582adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
583adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5842c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    /**
5852c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson     * Returns a locale with the most-specific field removed, or null if this
5862c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson     * locale had an empty language, country and variant.
5872c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson     */
5882c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    private static Locale strip(Locale locale) {
5892c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        String language = locale.getLanguage();
5902c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        String country = locale.getCountry();
5912c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        String variant = locale.getVariant();
5922c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        if (!variant.isEmpty()) {
5932c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            variant = "";
5942c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        } else if (!country.isEmpty()) {
5952c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            country = "";
5962c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        } else if (!language.isEmpty()) {
5972c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            language = "";
5982c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        } else {
5992c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson            return null;
600adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
6012c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        return new Locale(language, country, variant);
602adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
603adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
604565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private void setLocale(Locale locale) {
605565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        this.locale = locale;
606565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
607565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
6082c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static void clearCache() {
609565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        cache.remove(ClassLoader.getSystemClassLoader());
610565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
611565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
6122c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson    public static void clearCache(ClassLoader loader) {
613b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (loader == null) {
61486acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("loader == null");
615565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
616565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        cache.remove(loader);
617565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
618565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
619565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public boolean containsKey(String key) {
620b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (key == null) {
62186acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("key == null");
622565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
623565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return keySet().contains(key);
624565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
625565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
626565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public Set<String> keySet() {
627565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        Set<String> ret = new HashSet<String>();
628565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        Enumeration<String> keys = getKeys();
629565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        while (keys.hasMoreElements()) {
630565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ret.add(keys.nextElement());
631565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
632565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return ret;
633565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
634565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
635565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    protected Set<String> handleKeySet() {
636565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        Set<String> set = keySet();
637565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        Set<String> ret = new HashSet<String>();
638565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        for (String key : set) {
639b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (handleGetObject(key) != null) {
640565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.add(key);
641565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
642565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
643565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return ret;
644565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
645565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
646565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private static class NoFallbackControl extends Control {
647565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
648565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static final Control NOFALLBACK_FORMAT_PROPERTIES_CONTROL = new NoFallbackControl(
649565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                JAVAPROPERTIES);
650565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
651565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static final Control NOFALLBACK_FORMAT_CLASS_CONTROL = new NoFallbackControl(
652565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                JAVACLASS);
653565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
654565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static final Control NOFALLBACK_FORMAT_DEFAULT_CONTROL = new NoFallbackControl(
655565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                listDefault);
656565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
657565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public NoFallbackControl(String format) {
658565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass = new ArrayList<String>();
659565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass.add(format);
660565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            super.format = Collections.unmodifiableList(listClass);
661565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
662565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
663565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public NoFallbackControl(List<String> list) {
664565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            super.format = list;
665565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
666565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
667565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        @Override
668565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public Locale getFallbackLocale(String baseName, Locale locale) {
66986acc043d3334651ee26c65467d78d6cefedd397Kenny Root            if (baseName == null) {
67086acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
67186acc043d3334651ee26c65467d78d6cefedd397Kenny Root            } else if (locale == null) {
67286acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("locale == null");
673565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
674565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return null;
675565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
676565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
677565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
678565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    private static class SimpleControl extends Control {
679565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public SimpleControl(String format) {
680565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass = new ArrayList<String>();
681565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass.add(format);
682565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            super.format = Collections.unmodifiableList(listClass);
683565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
684565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
685565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
686565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
687565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * ResourceBundle.Control is a static utility class defines ResourceBundle
688565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * load access methods, its default access order is as the same as before.
689565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * However users can implement their own control.
690f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
691565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
692565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
693565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public static class Control {
694565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static List<String> listDefault = new ArrayList<String>();
695565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
696565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static List<String> listClass = new ArrayList<String>();
697565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
698565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static List<String> listProperties = new ArrayList<String>();
699565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
700565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static String JAVACLASS = "java.class";
701565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
702565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static String JAVAPROPERTIES = "java.properties";
703565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
704565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        static {
705565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listDefault.add(JAVACLASS);
706565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listDefault.add(JAVAPROPERTIES);
707565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass.add(JAVACLASS);
708565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listProperties.add(JAVAPROPERTIES);
709565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
710565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
711565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
712565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a list defines default format
713565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
714565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public static final List<String> FORMAT_DEFAULT = Collections
715565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                .unmodifiableList(listDefault);
716565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
717565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
718565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a list defines java class format
719565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
720565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public static final List<String> FORMAT_CLASS = Collections
721565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                .unmodifiableList(listClass);
722565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
723565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
724565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a list defines property format
725565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
726565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public static final List<String> FORMAT_PROPERTIES = Collections
727565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                .unmodifiableList(listProperties);
728565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
729565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
730565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a constant that indicates cache will not be used.
731565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
732565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public static final long TTL_DONT_CACHE = -1L;
733565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
734565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
735565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a constant that indicates cache will not be expired.
736565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
737565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public static final long TTL_NO_EXPIRATION_CONTROL = -2L;
738565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
739565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        private static final Control FORMAT_PROPERTIES_CONTROL = new SimpleControl(
740565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                JAVAPROPERTIES);
741565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
742565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        private static final Control FORMAT_CLASS_CONTROL = new SimpleControl(
743565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                JAVACLASS);
744565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
745565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        private static final Control FORMAT_DEFAULT_CONTROL = new Control();
746565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
747565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        List<String> format;
748565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
749565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
750565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * default constructor
751f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         *
752565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
753565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        protected Control() {
754565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass = new ArrayList<String>();
755565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass.add(JAVACLASS);
756565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            listClass.add(JAVAPROPERTIES);
757565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            format = Collections.unmodifiableList(listClass);
758565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
759565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
760565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
761eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns a control according to {@code formats}.
762565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
7632c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        public static Control getControl(List<String> formats) {
764565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            switch (formats.size()) {
765565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            case 1:
766565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.contains(JAVACLASS)) {
767565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return FORMAT_CLASS_CONTROL;
768565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
769565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.contains(JAVAPROPERTIES)) {
770565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return FORMAT_PROPERTIES_CONTROL;
771565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
772565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                break;
773565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            case 2:
774565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.equals(FORMAT_DEFAULT)) {
775565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return FORMAT_DEFAULT_CONTROL;
776565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
777565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                break;
778565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
779565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException();
780565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
781565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
782565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
783eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns a control according to {@code formats} whose fallback
784eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * locale is null.
785565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
7862c98427a50610d4991e9b0ada6cbc7aceb194580Jesse Wilson        public static Control getNoFallbackControl(List<String> formats) {
787565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            switch (formats.size()) {
788565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            case 1:
789565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.contains(JAVACLASS)) {
790565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return NoFallbackControl.NOFALLBACK_FORMAT_CLASS_CONTROL;
791565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
792565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.contains(JAVAPROPERTIES)) {
793565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return NoFallbackControl.NOFALLBACK_FORMAT_PROPERTIES_CONTROL;
794565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
795565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                break;
796565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            case 2:
797565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (formats.equals(FORMAT_DEFAULT)) {
798565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return NoFallbackControl.NOFALLBACK_FORMAT_DEFAULT_CONTROL;
799565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
800565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                break;
801565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
802565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException();
803565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
804565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
805565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
806eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns a list of candidate locales according to {@code baseName} in
807eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * {@code locale}.
808565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
809565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public List<Locale> getCandidateLocales(String baseName, Locale locale) {
81086acc043d3334651ee26c65467d78d6cefedd397Kenny Root            if (baseName == null) {
81186acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
81286acc043d3334651ee26c65467d78d6cefedd397Kenny Root            } else if (locale == null) {
81386acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("locale == null");
814565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
815565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            List<Locale> retList = new ArrayList<Locale>();
816565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String language = locale.getLanguage();
817565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String country = locale.getCountry();
818565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String variant = locale.getVariant();
819565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!EMPTY_STRING.equals(variant)) {
820565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                retList.add(new Locale(language, country, variant));
821565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
822565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!EMPTY_STRING.equals(country)) {
823565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                retList.add(new Locale(language, country));
824565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
825565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!EMPTY_STRING.equals(language)) {
826565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                retList.add(new Locale(language));
827565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
828565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            retList.add(Locale.ROOT);
829565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return retList;
830565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
831565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
832565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
833eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns a list of strings of formats according to {@code baseName}.
834565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
835565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public List<String> getFormats(String baseName) {
836b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (baseName == null) {
83786acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
838565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
839565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return format;
840565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
841565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
842565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
843eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns the fallback locale for {@code baseName} in {@code locale}.
844565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
845565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public Locale getFallbackLocale(String baseName, Locale locale) {
84686acc043d3334651ee26c65467d78d6cefedd397Kenny Root            if (baseName == null) {
84786acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
84886acc043d3334651ee26c65467d78d6cefedd397Kenny Root            } else if (locale == null) {
84986acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("locale == null");
850565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
851565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (Locale.getDefault() != locale) {
852565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                return Locale.getDefault();
853565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
854565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return null;
855565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
856565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
857565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
858eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns a new ResourceBundle.
859f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         *
860565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param baseName
861565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the base name to use
862565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param locale
863565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the given locale
864565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param format
865eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         *            the format, default is "java.class" or "java.properties"
866565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param loader
867565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the classloader to use
868565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param reload
869eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         *            whether to reload the resource
870565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @return a new ResourceBundle according to the give parameters
871565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @throws IllegalAccessException
872eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         *             if we can not access resources
873565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @throws InstantiationException
874eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         *             if we can not instantiate a resource class
875565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @throws IOException
876565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *             if other I/O exception happens
877565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
878565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public ResourceBundle newBundle(String baseName, Locale locale,
879565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                String format, ClassLoader loader, boolean reload)
880565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                throws IllegalAccessException, InstantiationException,
881565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                IOException {
88286acc043d3334651ee26c65467d78d6cefedd397Kenny Root            if (format == null) {
88386acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("format == null");
88486acc043d3334651ee26c65467d78d6cefedd397Kenny Root            } else if (loader == null) {
88586acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("loader == null");
886565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
887565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            final String bundleName = toBundleName(baseName, locale);
888565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            final ClassLoader clsloader = loader;
889565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ResourceBundle ret;
890510dc6afb60f9099127c5bc9fb91d86b778d747fElliott Hughes            if (format.equals(JAVACLASS)) {
891ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                Class<?> cls = null;
892ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                try {
893ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                    cls = clsloader.loadClass(bundleName);
894ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                } catch (Exception e) {
895ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                } catch (NoClassDefFoundError e) {
896ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                }
897b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                if (cls == null) {
898565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return null;
899565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
900565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                try {
901565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    ResourceBundle bundle = (ResourceBundle) cls.newInstance();
902565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    bundle.setLocale(locale);
903565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return bundle;
904565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                } catch (NullPointerException e) {
905565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return null;
906565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
907565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
908510dc6afb60f9099127c5bc9fb91d86b778d747fElliott Hughes            if (format.equals(JAVAPROPERTIES)) {
909ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                InputStream streams = null;
910ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                final String resourceName = toResourceName(bundleName, "properties");
911565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (reload) {
912565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    URL url = null;
913565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    try {
914565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        url = loader.getResource(resourceName);
915565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    } catch (NullPointerException e) {
916565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        // do nothing
917565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
918b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                    if (url != null) {
919565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        URLConnection con = url.openConnection();
920565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        con.setUseCaches(false);
921565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        streams = con.getInputStream();
922565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
923565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                } else {
924565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    try {
925ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes                        streams = clsloader.getResourceAsStream(resourceName);
926565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    } catch (NullPointerException e) {
927565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        // do nothing
928565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
929565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
930565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (streams != null) {
931565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    try {
932565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        ret = new PropertyResourceBundle(new InputStreamReader(streams));
933565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        ret.setLocale(locale);
934565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        streams.close();
935565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    } catch (IOException e) {
936565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        return null;
937565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    }
938565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return ret;
939565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
940565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                return null;
941565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
942565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException();
943565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
944565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
945565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
946eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns the time to live of the ResourceBundle {@code baseName} in {@code locale},
947eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * default is TTL_NO_EXPIRATION_CONTROL.
948565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
949565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public long getTimeToLive(String baseName, Locale locale) {
95086acc043d3334651ee26c65467d78d6cefedd397Kenny Root            if (baseName == null) {
95186acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
95286acc043d3334651ee26c65467d78d6cefedd397Kenny Root            } else if (locale == null) {
95386acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("locale == null");
954565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
955565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return TTL_NO_EXPIRATION_CONTROL;
956565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
957565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
958565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
959eeefcae2980c8db05ec08303b5b112afce232d26Elliott Hughes         * Returns true if the ResourceBundle needs to reload.
960f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         *
961565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param baseName
962565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the base name of the ResourceBundle
963565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param locale
964565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the locale of the ResourceBundle
965565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param format
966565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the format to load
967565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param loader
968565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the ClassLoader to load resource
969565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param bundle
970565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the ResourceBundle
971565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param loadTime
972565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the expired time
973565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @return if the ResourceBundle needs to reload
974565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
975565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public boolean needsReload(String baseName, Locale locale,
976565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                String format, ClassLoader loader, ResourceBundle bundle,
977565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                long loadTime) {
978b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (bundle == null) {
979565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                // FIXME what's the use of bundle?
98086acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("bundle == null");
981565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
982565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String bundleName = toBundleName(baseName, locale);
983565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String suffix = format;
984510dc6afb60f9099127c5bc9fb91d86b778d747fElliott Hughes            if (format.equals(JAVACLASS)) {
985565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                suffix = "class";
986565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
987510dc6afb60f9099127c5bc9fb91d86b778d747fElliott Hughes            if (format.equals(JAVAPROPERTIES)) {
988565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                suffix = "properties";
989565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
990565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String urlname = toResourceName(bundleName, suffix);
991565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            URL url = loader.getResource(urlname);
992b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (url != null) {
993565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                String fileName = url.getFile();
994565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                long lastModified = new File(fileName).lastModified();
995565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                if (lastModified > loadTime) {
996565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    return true;
997565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                }
998565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
999565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return false;
1000565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
1001565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
1002565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
1003565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a utility method to answer the name of a resource bundle according to
1004565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * the given base name and locale
1005f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         *
1006565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param baseName
1007565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the given base name
1008565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param locale
1009565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the locale to use
1010565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @return the name of a resource bundle according to the given base
1011565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *         name and locale
1012565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
1013565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public String toBundleName(String baseName, Locale locale) {
1014565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            final String emptyString = EMPTY_STRING;
1015565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            final String preString = UNDER_SCORE;
1016565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            final String underline = UNDER_SCORE;
1017b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (baseName == null) {
101886acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("baseName == null");
1019565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
1020565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            StringBuilder ret = new StringBuilder();
1021565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            StringBuilder prefix = new StringBuilder();
1022565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ret.append(baseName);
1023565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!locale.getLanguage().equals(emptyString)) {
1024565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(underline);
1025565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(locale.getLanguage());
1026565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            } else {
1027565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                prefix.append(preString);
1028565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
1029565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!locale.getCountry().equals(emptyString)) {
1030565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append((CharSequence) prefix);
1031565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(underline);
1032565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(locale.getCountry());
1033565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                prefix = new StringBuilder();
1034565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            } else {
1035565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                prefix.append(preString);
1036565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
1037565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (!locale.getVariant().equals(emptyString)) {
1038565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append((CharSequence) prefix);
1039565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(underline);
1040565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                ret.append(locale.getVariant());
1041565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
1042565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return ret.toString();
1043565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
1044565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
1045565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        /**
1046565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * a utility method to answer the name of a resource according to the
1047565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * given bundleName and suffix
1048f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes         *
1049565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param bundleName
1050565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the given bundle name
1051565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @param suffix
1052565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *            the suffix
1053565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         * @return the name of a resource according to the given bundleName and
1054565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         *         suffix
1055565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes         */
1056565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        public final String toResourceName(String bundleName, String suffix) {
1057b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes            if (suffix == null) {
105886acc043d3334651ee26c65467d78d6cefedd397Kenny Root                throw new NullPointerException("suffix == null");
1059565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
1060565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            StringBuilder ret = new StringBuilder(bundleName.replace('.', '/'));
1061565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ret.append('.');
1062565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            ret.append(suffix);
1063565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            return ret.toString();
1064565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
1065adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1066adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
1067