001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle;
021
022import java.text.MessageFormat;
023import java.util.Locale;
024import java.util.MissingResourceException;
025import java.util.ResourceBundle;
026
027import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
028
029/**
030 * Represents a message that can be localised. The translations come from
031 * message.properties files. The underlying implementation uses
032 * java.text.MessageFormat.
033 */
034public class LocalizedMessage {
035
036    /** The locale to localise messages to. */
037    private static Locale messageLocale = Locale.getDefault();
038
039    /** Name of the resource bundle to get messages from. */
040    private final String bundle;
041
042    /** Class of the source for this message. */
043    private final Class<?> sourceClass;
044
045    /**
046     * Key for the message format.
047     */
048    private final String key;
049
050    /**
051     * Arguments for java.text.MessageFormat, that is why type is Object[].
052     *
053     * <p>Note: Changing types from Object[] will be huge breaking compatibility, as Module
054     * messages use some type formatting already, so better to keep it as Object[].
055     * </p>
056     */
057    private final Object[] args;
058
059    /**
060     * Creates a new {@code LocalizedMessage} instance.
061     *
062     * @param bundle resource bundle name
063     * @param sourceClass the Class that is the source of the message
064     * @param key the key to locate the translation.
065     * @param args arguments for the translation.
066     */
067    public LocalizedMessage(String bundle, Class<?> sourceClass, String key,
068            Object... args) {
069        this.bundle = bundle;
070        this.sourceClass = sourceClass;
071        this.key = key;
072        if (args == null) {
073            this.args = null;
074        }
075        else {
076            this.args = UnmodifiableCollectionUtil.copyOfArray(args, args.length);
077        }
078    }
079
080    /**
081     * Sets a locale to use for localization.
082     *
083     * @param locale the locale to use for localization
084     */
085    public static void setLocale(Locale locale) {
086        if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
087            messageLocale = Locale.ROOT;
088        }
089        else {
090            messageLocale = locale;
091        }
092    }
093
094    /**
095     * Gets the translated message.
096     *
097     * @return the translated message.
098     */
099    public String getMessage() {
100        String result;
101        try {
102            // Important to use the default class loader, and not the one in
103            // the GlobalProperties object. This is because the class loader in
104            // the GlobalProperties is specified by the user for resolving
105            // custom classes.
106            final ResourceBundle resourceBundle = getBundle();
107            final String pattern = resourceBundle.getString(key);
108            final MessageFormat formatter = new MessageFormat(pattern, Locale.ROOT);
109            result = formatter.format(args);
110        }
111        catch (final MissingResourceException ignored) {
112            // If the Check author didn't provide i18n resource bundles
113            // and logs audit event messages directly, this will return
114            // the author's original message
115            final MessageFormat formatter = new MessageFormat(key, Locale.ROOT);
116            result = formatter.format(args);
117        }
118        return result;
119    }
120
121    /**
122     * Obtain the ResourceBundle. Uses the classloader
123     * of the class emitting this message, to be sure to get the correct
124     * bundle. Property files are read as UTF-8 by the JDK since Java 9,
125     * so no custom {@code ResourceBundle.Control} is needed.
126     *
127     * @return a ResourceBundle.
128     */
129    private ResourceBundle getBundle() {
130        return ResourceBundle.getBundle(bundle, messageLocale, sourceClass.getClassLoader());
131    }
132
133}