src/share/jaxws_classes/com/sun/xml/internal/dtdparser/MessageCatalog.java

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/dtdparser/MessageCatalog.java	Wed Apr 27 01:27:09 2016 +0800
     1.3 @@ -0,0 +1,516 @@
     1.4 +/*
     1.5 + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Oracle designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Oracle in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.25 + * or visit www.oracle.com if you need additional information or have any
    1.26 + * questions.
    1.27 + */
    1.28 +
    1.29 +package com.sun.xml.internal.dtdparser;
    1.30 +
    1.31 +import java.io.InputStream;
    1.32 +import java.text.FieldPosition;
    1.33 +import java.text.MessageFormat;
    1.34 +import java.util.Hashtable;
    1.35 +import java.util.Locale;
    1.36 +import java.util.MissingResourceException;
    1.37 +import java.util.ResourceBundle;
    1.38 +
    1.39 +
    1.40 +/**
    1.41 + * This class provides support for multi-language string lookup, as needed
    1.42 + * to localize messages from applications supporting multiple languages
    1.43 + * at the same time.  One class of such applications is network services,
    1.44 + * such as HTTP servers, which talk to clients who may not be from the
    1.45 + * same locale as the server.  This class supports a form of negotiation
    1.46 + * for the language used in presenting a message from some package, where
    1.47 + * both user (client) preferences and application (server) support are
    1.48 + * accounted for when choosing locales and formatting messages.
    1.49 + * <p/>
    1.50 + * <P> Each package should have a singleton package-private message catalog
    1.51 + * class.  This ensures that the correct class loader will always be used to
    1.52 + * access message resources, and minimizes use of memory: <PRE>
    1.53 + * package <em>some.package</em>;
    1.54 + * <p/>
    1.55 + * // "foo" might be public
    1.56 + * class foo {
    1.57 + * ...
    1.58 + * // package private
    1.59 + * static final Catalog messages = new Catalog ();
    1.60 + * static final class Catalog extends MessageCatalog {
    1.61 + * Catalog () { super (Catalog.class); }
    1.62 + * }
    1.63 + * ...
    1.64 + * }
    1.65 + * </PRE>
    1.66 + * <p/>
    1.67 + * <P> Messages for a known client could be generated using code
    1.68 + * something like this:  <PRE>
    1.69 + * String clientLanguages [];
    1.70 + * Locale clientLocale;
    1.71 + * String clientMessage;
    1.72 + * <p/>
    1.73 + * // client languages will probably be provided by client,
    1.74 + * // e.g. by an HTTP/1.1 "Accept-Language" header.
    1.75 + * clientLanguages = new String [] { "en-ca", "fr-ca", "ja", "zh" };
    1.76 + * clientLocale = foo.messages.chooseLocale (clientLanguages);
    1.77 + * clientMessage = foo.messages.getMessage (clientLocale,
    1.78 + * "fileCount",
    1.79 + * new Object [] { new Integer (numberOfFiles) }
    1.80 + * );
    1.81 + * </PRE>
    1.82 + * <p/>
    1.83 + * <P> At this time, this class does not include functionality permitting
    1.84 + * messages to be passed around and localized after-the-fact.  The consequence
    1.85 + * of this is that the locale for messages must be passed down through layers
    1.86 + * which have no normal reason to support such passdown, or else the system
    1.87 + * default locale must be used instead of the one the client needs.
    1.88 + * <p/>
    1.89 + * <P> <hr> The following guidelines should be used when constructiong
    1.90 + * multi-language applications:  <OL>
    1.91 + * <p/>
    1.92 + * <LI> Always use <a href=#chooseLocale>chooseLocale</a> to select the
    1.93 + * locale you pass to your <code>getMessage</code> call.  This lets your
    1.94 + * applications use IETF standard locale names, and avoids needless
    1.95 + * use of system defaults.
    1.96 + * <p/>
    1.97 + * <LI> The localized messages for a given package should always go in
    1.98 + * a separate <em>resources</em> sub-package.  There are security
    1.99 + * implications; see below.
   1.100 + * <p/>
   1.101 + * <LI> Make sure that a language name is included in each bundle name,
   1.102 + * so that the developer's locale will not be inadvertently used. That
   1.103 + * is, don't create defaults like <em>resources/Messages.properties</em>
   1.104 + * or <em>resources/Messages.class</em>, since ResourceBundle will choose
   1.105 + * such defaults rather than giving software a chance to choose a more
   1.106 + * appropriate language for its messages.  Your message bundles should
   1.107 + * have names like <em>Messages_en.properties</em> (for the "en", or
   1.108 + * English, language) or <em>Messages_ja.class</em> ("ja" indicates the
   1.109 + * Japanese language).
   1.110 + * <p/>
   1.111 + * <LI> Only use property files for messages in languages which can
   1.112 + * be limited to the ISO Latin/1 (8859-1) characters supported by the
   1.113 + * property file format.  (This is mostly Western European languages.)
   1.114 + * Otherwise, subclass ResourceBundle to provide your messages; it is
   1.115 + * simplest to subclass <code>java.util.ListResourceBundle</code>.
   1.116 + * <p/>
   1.117 + * <LI> Never use another package's message catalog or resource bundles.
   1.118 + * It should not be possible for a change internal to one package (such
   1.119 + * as eliminating or improving messages) to break another package.
   1.120 + * <p/>
   1.121 + * </OL>
   1.122 + * <p/>
   1.123 + * <P> The "resources" sub-package can be treated separately from the
   1.124 + * package with which it is associated.  That main package may be sealed
   1.125 + * and possibly signed, preventing other software from adding classes to
   1.126 + * the package which would be able to access methods and data which are
   1.127 + * not designed to be publicly accessible.  On the other hand, resources
   1.128 + * such as localized messages are often provided after initial product
   1.129 + * shipment, without a full release cycle for the product.  Such files
   1.130 + * (text and class files) need to be added to some package.  Since they
   1.131 + * should not be added to the main package, the "resources" subpackage is
   1.132 + * used without risking the security or integrity of that main package
   1.133 + * as distributed in its JAR file.
   1.134 + *
   1.135 + * @author David Brownell
   1.136 + * @version 1.1, 00/08/05
   1.137 + * @see java.util.Locale
   1.138 + * @see java.util.ListResourceBundle
   1.139 + * @see java.text.MessageFormat
   1.140 + */
   1.141 +// leave this as "abstract" -- each package needs its own subclass,
   1.142 +// else it's not always going to be using the right class loader.
   1.143 +abstract public class MessageCatalog {
   1.144 +    private String bundleName;
   1.145 +
   1.146 +    /**
   1.147 +     * Create a message catalog for use by classes in the same package
   1.148 +     * as the specified class.  This uses <em>Messages</em> resource
   1.149 +     * bundles in the <em>resources</em> sub-package of class passed as
   1.150 +     * a parameter.
   1.151 +     *
   1.152 +     * @param packageMember Class whose package has localized messages
   1.153 +     */
   1.154 +    protected MessageCatalog(Class packageMember) {
   1.155 +        this(packageMember, "Messages");
   1.156 +    }
   1.157 +
   1.158 +    /**
   1.159 +     * Create a message catalog for use by classes in the same package
   1.160 +     * as the specified class.  This uses the specified resource
   1.161 +     * bundle name in the <em>resources</em> sub-package of class passed
   1.162 +     * as a parameter; for example, <em>resources.Messages</em>.
   1.163 +     *
   1.164 +     * @param packageMember Class whose package has localized messages
   1.165 +     * @param bundle        Name of a group of resource bundles
   1.166 +     */
   1.167 +    private MessageCatalog(Class packageMember, String bundle) {
   1.168 +        int index;
   1.169 +
   1.170 +        bundleName = packageMember.getName();
   1.171 +        index = bundleName.lastIndexOf('.');
   1.172 +        if (index == -1)    // "ClassName"
   1.173 +            bundleName = "";
   1.174 +        else            // "some.package.ClassName"
   1.175 +            bundleName = bundleName.substring(0, index) + ".";
   1.176 +        bundleName = bundleName + "resources." + bundle;
   1.177 +    }
   1.178 +
   1.179 +
   1.180 +    /**
   1.181 +     * Get a message localized to the specified locale, using the message ID
   1.182 +     * and package name if no message is available.  The locale is normally
   1.183 +     * that of the client of a service, chosen with knowledge that both the
   1.184 +     * client and this server support that locale.  There are two error
   1.185 +     * cases:  first, when the specified locale is unsupported or null, the
   1.186 +     * default locale is used if possible; second, when no bundle supports
   1.187 +     * that locale, the message ID and package name are used.
   1.188 +     *
   1.189 +     * @param locale    The locale of the message to use.  If this is null,
   1.190 +     *                  the default locale will be used.
   1.191 +     * @param messageId The ID of the message to use.
   1.192 +     * @return The message, localized as described above.
   1.193 +     */
   1.194 +    public String getMessage(Locale locale,
   1.195 +                             String messageId) {
   1.196 +        ResourceBundle bundle;
   1.197 +
   1.198 +        // cope with unsupported locale...
   1.199 +        if (locale == null)
   1.200 +            locale = Locale.getDefault();
   1.201 +
   1.202 +        try {
   1.203 +            bundle = ResourceBundle.getBundle(bundleName, locale);
   1.204 +        } catch (MissingResourceException e) {
   1.205 +            bundle = ResourceBundle.getBundle(bundleName, Locale.ENGLISH);
   1.206 +        }
   1.207 +        return bundle.getString(messageId);
   1.208 +    }
   1.209 +
   1.210 +
   1.211 +    /**
   1.212 +     * Format a message localized to the specified locale, using the message
   1.213 +     * ID with its package name if none is available.  The locale is normally
   1.214 +     * the client of a service, chosen with knowledge that both the client
   1.215 +     * server support that locale.  There are two error cases:  first, if the
   1.216 +     * specified locale is unsupported or null, the default locale is used if
   1.217 +     * possible; second, when no bundle supports that locale, the message ID
   1.218 +     * and package name are used.
   1.219 +     *
   1.220 +     * @param locale     The locale of the message to use.  If this is null,
   1.221 +     *                   the default locale will be used.
   1.222 +     * @param messageId  The ID of the message format to use.
   1.223 +     * @param parameters Used when formatting the message.  Objects in
   1.224 +     *                   this list are turned to strings if they are not Strings, Numbers,
   1.225 +     *                   or Dates (that is, if MessageFormat would treat them as errors).
   1.226 +     * @return The message, localized as described above.
   1.227 +     * @see java.text.MessageFormat
   1.228 +     */
   1.229 +    public String getMessage(Locale locale,
   1.230 +                             String messageId,
   1.231 +                             Object parameters []) {
   1.232 +        if (parameters == null)
   1.233 +            return getMessage(locale, messageId);
   1.234 +
   1.235 +        // since most messages won't be tested (sigh), be friendly to
   1.236 +        // the inevitable developer errors of passing random data types
   1.237 +        // to the message formatting code.
   1.238 +        for (int i = 0; i < parameters.length; i++) {
   1.239 +            if (!(parameters[i] instanceof String)
   1.240 +                    && !(parameters[i] instanceof Number)
   1.241 +                    && !(parameters[i] instanceof java.util.Date)) {
   1.242 +                if (parameters[i] == null)
   1.243 +                    parameters[i] = "(null)";
   1.244 +                else
   1.245 +                    parameters[i] = parameters[i].toString();
   1.246 +            }
   1.247 +        }
   1.248 +
   1.249 +        // similarly, cope with unsupported locale...
   1.250 +        if (locale == null)
   1.251 +            locale = Locale.getDefault();
   1.252 +
   1.253 +        // get the appropriately localized MessageFormat object
   1.254 +        ResourceBundle bundle;
   1.255 +        MessageFormat format;
   1.256 +
   1.257 +        try {
   1.258 +            bundle = ResourceBundle.getBundle(bundleName, locale);
   1.259 +        } catch (MissingResourceException e) {
   1.260 +            bundle = ResourceBundle.getBundle(bundleName, Locale.ENGLISH);
   1.261 +            /*String retval;
   1.262 +
   1.263 +            retval = packagePrefix (messageId);
   1.264 +            for (int i = 0; i < parameters.length; i++) {
   1.265 +            retval += ' ';
   1.266 +            retval += parameters [i];
   1.267 +            }
   1.268 +            return retval;*/
   1.269 +        }
   1.270 +        format = new MessageFormat(bundle.getString(messageId));
   1.271 +        format.setLocale(locale);
   1.272 +
   1.273 +        // return the formatted message
   1.274 +        StringBuffer result = new StringBuffer();
   1.275 +
   1.276 +        result = format.format(parameters, result, new FieldPosition(0));
   1.277 +        return result.toString();
   1.278 +    }
   1.279 +
   1.280 +
   1.281 +    /**
   1.282 +     * Chooses a client locale to use, using the first language specified in
   1.283 +     * the list that is supported by this catalog.  If none of the specified
   1.284 +     * languages is supported, a null value is returned.  Such a list of
   1.285 +     * languages might be provided in an HTTP/1.1 "Accept-Language" header
   1.286 +     * field, or through some other content negotiation mechanism.
   1.287 +     * <p/>
   1.288 +     * <P> The language specifiers recognized are RFC 1766 style ("fr" for
   1.289 +     * all French, "fr-ca" for Canadian French), although only the strict
   1.290 +     * ISO subset (two letter language and country specifiers) is currently
   1.291 +     * supported.  Java-style locale strings ("fr_CA") are also supported.
   1.292 +     *
   1.293 +     * @param languages Array of language specifiers, ordered with the most
   1.294 +     *                  preferable one at the front.  For example, "en-ca" then "fr-ca",
   1.295 +     *                  followed by "zh_CN".
   1.296 +     * @return The most preferable supported locale, or null.
   1.297 +     * @see java.util.Locale
   1.298 +     */
   1.299 +    public Locale chooseLocale(String languages []) {
   1.300 +        if ((languages = canonicalize(languages)) != null) {
   1.301 +            for (int i = 0; i < languages.length; i++)
   1.302 +                if (isLocaleSupported(languages[i]))
   1.303 +                    return getLocale(languages[i]);
   1.304 +        }
   1.305 +        return null;
   1.306 +    }
   1.307 +
   1.308 +
   1.309 +    //
   1.310 +    // Canonicalizes the RFC 1766 style language strings ("en-in") to
   1.311 +    // match standard Java usage ("en_IN"), removing strings that don't
   1.312 +    // use two character ISO language and country codes.   Avoids all
   1.313 +    // memory allocations possible, so that if the strings passed in are
   1.314 +    // just lowercase ISO codes (a common case) the input is returned.
   1.315 +    //
   1.316 +    private String[] canonicalize(String languages []) {
   1.317 +        boolean didClone = false;
   1.318 +        int trimCount = 0;
   1.319 +
   1.320 +        if (languages == null)
   1.321 +            return languages;
   1.322 +
   1.323 +        for (int i = 0; i < languages.length; i++) {
   1.324 +            String lang = languages[i];
   1.325 +            int len = lang.length();
   1.326 +
   1.327 +            // no RFC1766 extensions allowed; "zh" and "zh-tw" (etc) are OK
   1.328 +            // as are regular locale names with no variant ("de_CH").
   1.329 +            if (!(len == 2 || len == 5)) {
   1.330 +                if (!didClone) {
   1.331 +                    languages = (String[]) languages.clone();
   1.332 +                    didClone = true;
   1.333 +                }
   1.334 +                languages[i] = null;
   1.335 +                trimCount++;
   1.336 +                continue;
   1.337 +            }
   1.338 +
   1.339 +            // language code ... if already lowercase, we change nothing
   1.340 +            if (len == 2) {
   1.341 +                lang = lang.toLowerCase();
   1.342 +                if (lang != languages[i]) {
   1.343 +                    if (!didClone) {
   1.344 +                        languages = (String[]) languages.clone();
   1.345 +                        didClone = true;
   1.346 +                    }
   1.347 +                    languages[i] = lang;
   1.348 +                }
   1.349 +                continue;
   1.350 +            }
   1.351 +
   1.352 +            // language_country ... fixup case, force "_"
   1.353 +            char buf [] = new char[5];
   1.354 +
   1.355 +            buf[0] = Character.toLowerCase(lang.charAt(0));
   1.356 +            buf[1] = Character.toLowerCase(lang.charAt(1));
   1.357 +            buf[2] = '_';
   1.358 +            buf[3] = Character.toUpperCase(lang.charAt(3));
   1.359 +            buf[4] = Character.toUpperCase(lang.charAt(4));
   1.360 +            if (!didClone) {
   1.361 +                languages = (String[]) languages.clone();
   1.362 +                didClone = true;
   1.363 +            }
   1.364 +            languages[i] = new String(buf);
   1.365 +        }
   1.366 +
   1.367 +        // purge any shadows of deleted RFC1766 extended language codes
   1.368 +        if (trimCount != 0) {
   1.369 +            String temp [] = new String[languages.length - trimCount];
   1.370 +            int i;
   1.371 +
   1.372 +            for (i = 0, trimCount = 0; i < temp.length; i++) {
   1.373 +                while (languages[i + trimCount] == null)
   1.374 +                    trimCount++;
   1.375 +                temp[i] = languages[i + trimCount];
   1.376 +            }
   1.377 +            languages = temp;
   1.378 +        }
   1.379 +        return languages;
   1.380 +    }
   1.381 +
   1.382 +
   1.383 +    //
   1.384 +    // Returns a locale object supporting the specified locale, using
   1.385 +    // a small cache to speed up some common languages and reduce the
   1.386 +    // needless allocation of memory.
   1.387 +    //
   1.388 +    private Locale getLocale(String localeName) {
   1.389 +        String language, country;
   1.390 +        int index;
   1.391 +
   1.392 +        index = localeName.indexOf('_');
   1.393 +        if (index == -1) {
   1.394 +            //
   1.395 +            // Special case the builtin JDK languages
   1.396 +            //
   1.397 +            if (localeName.equals("de"))
   1.398 +                return Locale.GERMAN;
   1.399 +            if (localeName.equals("en"))
   1.400 +                return Locale.ENGLISH;
   1.401 +            if (localeName.equals("fr"))
   1.402 +                return Locale.FRENCH;
   1.403 +            if (localeName.equals("it"))
   1.404 +                return Locale.ITALIAN;
   1.405 +            if (localeName.equals("ja"))
   1.406 +                return Locale.JAPANESE;
   1.407 +            if (localeName.equals("ko"))
   1.408 +                return Locale.KOREAN;
   1.409 +            if (localeName.equals("zh"))
   1.410 +                return Locale.CHINESE;
   1.411 +
   1.412 +            language = localeName;
   1.413 +            country = "";
   1.414 +        } else {
   1.415 +            if (localeName.equals("zh_CN"))
   1.416 +                return Locale.SIMPLIFIED_CHINESE;
   1.417 +            if (localeName.equals("zh_TW"))
   1.418 +                return Locale.TRADITIONAL_CHINESE;
   1.419 +
   1.420 +            //
   1.421 +            // JDK also has constants for countries:  en_GB, en_US, en_CA,
   1.422 +            // fr_FR, fr_CA, de_DE, ja_JP, ko_KR.  We don't use those.
   1.423 +            //
   1.424 +            language = localeName.substring(0, index);
   1.425 +            country = localeName.substring(index + 1);
   1.426 +        }
   1.427 +
   1.428 +        return new Locale(language, country);
   1.429 +    }
   1.430 +
   1.431 +
   1.432 +    //
   1.433 +    // cache for isLanguageSupported(), below ... key is a language
   1.434 +    // or locale name, value is a Boolean
   1.435 +    //
   1.436 +    private Hashtable cache = new Hashtable(5);
   1.437 +
   1.438 +
   1.439 +    /**
   1.440 +     * Returns true iff the specified locale has explicit language support.
   1.441 +     * For example, the traditional Chinese locale "zh_TW" has such support
   1.442 +     * if there are message bundles suffixed with either "zh_TW" or "zh".
   1.443 +     * <p/>
   1.444 +     * <P> This method is used to bypass part of the search path mechanism
   1.445 +     * of the <code>ResourceBundle</code> class, specifically the parts which
   1.446 +     * force use of default locales and bundles.  Such bypassing is required
   1.447 +     * in order to enable use of a client's preferred languages.  Following
   1.448 +     * the above example, if a client prefers "zh_TW" but can also accept
   1.449 +     * "ja", this method would be used to detect that there are no "zh_TW"
   1.450 +     * resource bundles and hence that "ja" messages should be used.  This
   1.451 +     * bypasses the ResourceBundle mechanism which will return messages in
   1.452 +     * some other locale (picking some hard-to-anticipate default) instead
   1.453 +     * of reporting an error and letting the client choose another locale.
   1.454 +     *
   1.455 +     * @param localeName A standard Java locale name, using two character
   1.456 +     *                   language codes optionally suffixed by country codes.
   1.457 +     * @return True iff the language of that locale is supported.
   1.458 +     * @see java.util.Locale
   1.459 +     */
   1.460 +    public boolean isLocaleSupported(String localeName) {
   1.461 +        //
   1.462 +        // Use previous results if possible.  We expect that the codebase
   1.463 +        // is immutable, so we never worry about changing the cache.
   1.464 +        //
   1.465 +        Boolean value = (Boolean) cache.get(localeName);
   1.466 +
   1.467 +        if (value != null)
   1.468 +            return value.booleanValue();
   1.469 +
   1.470 +        //
   1.471 +        // Try "language_country_variant", then "language_country",
   1.472 +        // then finally "language" ... assuming the longest locale name
   1.473 +        // is passed.  If not, we'll try fewer options.
   1.474 +        //
   1.475 +        ClassLoader loader = null;
   1.476 +
   1.477 +        for (; ;) {
   1.478 +            String name = bundleName + "_" + localeName;
   1.479 +
   1.480 +            // look up classes ...
   1.481 +            try {
   1.482 +                Class.forName(name);
   1.483 +                cache.put(localeName, Boolean.TRUE);
   1.484 +                return true;
   1.485 +            } catch (Exception e) {
   1.486 +            }
   1.487 +
   1.488 +            // ... then property files (only for ISO Latin/1 messages)
   1.489 +            InputStream in;
   1.490 +
   1.491 +            if (loader == null)
   1.492 +                loader = getClass().getClassLoader();
   1.493 +
   1.494 +            name = name.replace('.', '/');
   1.495 +            name = name + ".properties";
   1.496 +            if (loader == null)
   1.497 +                in = ClassLoader.getSystemResourceAsStream(name);
   1.498 +            else
   1.499 +                in = loader.getResourceAsStream(name);
   1.500 +            if (in != null) {
   1.501 +                cache.put(localeName, Boolean.TRUE);
   1.502 +                return true;
   1.503 +            }
   1.504 +
   1.505 +            int index = localeName.indexOf('_');
   1.506 +
   1.507 +            if (index > 0)
   1.508 +                localeName = localeName.substring(0, index);
   1.509 +            else
   1.510 +                break;
   1.511 +        }
   1.512 +
   1.513 +        //
   1.514 +        // If we got this far, we failed.  Remember for later.
   1.515 +        //
   1.516 +        cache.put(localeName, Boolean.FALSE);
   1.517 +        return false;
   1.518 +    }
   1.519 +}

mercurial