1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xml.internal.serializer.utils;
23
24import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
25import java.util.ListResourceBundle;
26import java.util.Locale;
27import java.util.MissingResourceException;
28import java.util.ResourceBundle;
29
30/**
31 * A utility class for issuing error messages.
32 *
33 * A user of this class normally would create a singleton
34 * instance of this class, passing the name
35 * of the message class on the constructor. For example:
36 * <CODE>
37 * static Messages x = new Messages("org.package.MyMessages");
38 * </CODE>
39 * Later the message is typically generated this way if there are no
40 * substitution arguments:
41 * <CODE>
42 * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null);
43 * </CODE>
44 * If there are arguments substitutions then something like this:
45 * <CODE>
46 * String filename = ...;
47 * String directory = ...;
48 * String msg = x.createMessage(org.package.MyMessages.KEY_TWO,
49 *   new Object[] {filename, directory) );
50 * </CODE>
51 *
52 * The constructor of an instance of this class must be given
53 * the class name of a class that extends java.util.ListResourceBundle
54 * ("org.package.MyMessages" in the example above).
55 * The name should not have any language suffix
56 * which will be added automatically by this utility class.
57 *
58 * The message class ("org.package.MyMessages")
59 * must define the abstract method getContents() that is
60 * declared in its base class, for example:
61 * <CODE>
62 * public Object[][] getContents() {return contents;}
63 * </CODE>
64 *
65 * It is suggested that the message class expose its
66 * message keys like this:
67 * <CODE>
68 *   public static final String KEY_ONE = "KEY1";
69 *   public static final String KEY_TWO = "KEY2";
70 *   . . .
71 * </CODE>
72 * and used through their names (KEY_ONE ...) rather than
73 * their values ("KEY1" ...).
74 *
75 * The field contents (returned by getContents()
76 * should be initialized something like this:
77 * <CODE>
78 * public static final Object[][] contents = {
79 * { KEY_ONE, "Something has gone wrong!" },
80 * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." },
81 * . . .
82 * { KEY_N, "Message N" }  }
83 * </CODE>
84 *
85 * Where that section of code with the KEY to Message mappings
86 * (where the message classes 'contents' field is initialized)
87 * can have the Message strings translated in an alternate language
88 * in a errorResourceClass with a language suffix.
89 *
90 *
91 * This class is not a public API, it is only public because it is
92 * used in com.sun.org.apache.xml.internal.serializer.
93 *
94 *  @xsl.usage internal
95 */
96public final class Messages
97{
98    /** The local object to use.  */
99    private final Locale m_locale = Locale.getDefault();
100
101    /** The language specific resource object for messages.  */
102    private ListResourceBundle m_resourceBundle;
103
104    /** The class name of the error message string table with no language suffix. */
105    private String m_resourceBundleName;
106
107
108
109    /**
110     * Constructor.
111     * @param resourceBundle the class name of the ListResourceBundle
112     * that the instance of this class is associated with and will use when
113     * creating messages.
114     * The class name is without a language suffix. If the value passed
115     * is null then loadResourceBundle(errorResourceClass) needs to be called
116     * explicitly before any messages are created.
117     *
118     * @xsl.usage internal
119     */
120    Messages(String resourceBundle)
121    {
122
123        m_resourceBundleName = resourceBundle;
124    }
125
126
127    /**
128     * Get the Locale object that is being used.
129     *
130     * @return non-null reference to Locale object.
131     * @xsl.usage internal
132     */
133    private Locale getLocale()
134    {
135        return m_locale;
136    }
137
138    /**
139     * Creates a message from the specified key and replacement
140     * arguments, localized to the given locale.
141     *
142     * @param msgKey  The key for the message text.
143     * @param args    The arguments to be used as replacement text
144     * in the message created.
145     *
146     * @return The formatted message string.
147     * @xsl.usage internal
148     */
149    public final String createMessage(String msgKey, Object args[])
150    {
151        if (m_resourceBundle == null)
152            m_resourceBundle = SecuritySupport.getResourceBundle(m_resourceBundleName);
153
154        if (m_resourceBundle != null)
155        {
156            return createMsg(m_resourceBundle, msgKey, args);
157        }
158        else
159            return "Could not load the resource bundles: "+ m_resourceBundleName;
160    }
161
162    /**
163     * Creates a message from the specified key and replacement
164     * arguments, localized to the given locale.
165     *
166     * @param errorCode The key for the message text.
167     *
168     * @param fResourceBundle The resource bundle to use.
169     * @param msgKey  The message key to use.
170     * @param args      The arguments to be used as replacement text
171     *                  in the message created.
172     *
173     * @return The formatted message string.
174     * @xsl.usage internal
175     */
176    private final String createMsg(
177        ListResourceBundle fResourceBundle,
178        String msgKey,
179        Object args[]) //throws Exception
180    {
181
182        String fmsg = null;
183        boolean throwex = false;
184        String msg = null;
185
186        if (msgKey != null)
187            msg = fResourceBundle.getString(msgKey);
188        else
189            msgKey = "";
190
191        if (msg == null)
192        {
193            throwex = true;
194            /* The message is not in the bundle . . . this is bad,
195             * so try to get the message that the message is not in the bundle
196             */
197            try
198            {
199
200                msg =
201                    java.text.MessageFormat.format(
202                        MsgKey.BAD_MSGKEY,
203                        new Object[] { msgKey, m_resourceBundleName });
204            }
205            catch (Exception e)
206            {
207                /* even the message that the message is not in the bundle is
208                 * not there ... this is really bad
209                 */
210                msg =
211                    "The message key '"
212                        + msgKey
213                        + "' is not in the message class '"
214                        + m_resourceBundleName+"'";
215            }
216        }
217        else if (args != null)
218        {
219            try
220            {
221                // Do this to keep format from crying.
222                // This is better than making a bunch of conditional
223                // code all over the place.
224                int n = args.length;
225
226                for (int i = 0; i < n; i++)
227                {
228                    if (null == args[i])
229                        args[i] = "";
230                }
231
232                fmsg = java.text.MessageFormat.format(msg, args);
233                // if we get past the line above we have create the message ... hurray!
234            }
235            catch (Exception e)
236            {
237                throwex = true;
238                try
239                {
240                    // Get the message that the format failed.
241                    fmsg =
242                        java.text.MessageFormat.format(
243                            MsgKey.BAD_MSGFORMAT,
244                            new Object[] { msgKey, m_resourceBundleName });
245                    fmsg += " " + msg;
246                }
247                catch (Exception formatfailed)
248                {
249                    // We couldn't even get the message that the format of
250                    // the message failed ... so fall back to English.
251                    fmsg =
252                        "The format of message '"
253                            + msgKey
254                            + "' in message class '"
255                            + m_resourceBundleName
256                            + "' failed.";
257                }
258            }
259        }
260        else
261            fmsg = msg;
262
263        if (throwex)
264        {
265            throw new RuntimeException(fmsg);
266        }
267
268        return fmsg;
269    }
270
271}
272