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