001: /*
002: Derby - Class org.apache.derby.common.i18n.MessageUtil
003:
004: Licensed to the Apache Software Foundation (ASF) under one or more
005: contributor license agreements. See the NOTICE file distributed with
006: this work for additional information regarding copyright ownership.
007: The ASF licenses this file to you under the Apache License, Version 2.0
008: (the "License"); you may not use this file except in compliance with
009: the License. You may obtain a copy of the License at
010:
011: http://www.apache.org/licenses/LICENSE-2.0
012:
013: Unless required by applicable law or agreed to in writing, software
014: distributed under the License is distributed on an "AS IS" BASIS,
015: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: See the License for the specific language governing permissions and
017: limitations under the License.
018:
019: */
020: package org.apache.derby.shared.common.i18n;
021:
022: import org.apache.derby.shared.common.error.ExceptionSeverity;
023: import org.apache.derby.shared.common.sanity.SanityManager;
024: import java.util.Locale;
025: import java.util.ResourceBundle;
026: import java.util.MissingResourceException;
027: import java.text.MessageFormat;
028:
029: /**
030: * Class comments here
031: */
032: public class MessageUtil {
033: public static final Locale US = new Locale("en", "US");
034:
035: /**
036: * The name of the resource bundle we are using to load
037: * messages
038: */
039: private String resourceBundleName;
040:
041: /**
042: * Create an instance of MessageUtil with a specific resource
043: * bundle. This assumes the default locale, which is just fine for
044: * users of this class other than the engine (which potentially has
045: * a different locale and a different resource bundle for each
046: * invocation of getCompleteMessage().
047: *
048: * @param resourceBundleName
049: * The base name of the resource bundle to use.
050: */
051: public MessageUtil(String resourceBundleName) {
052: this .resourceBundleName = resourceBundleName;
053: }
054:
055: /** Get a message with default locale - no arguments */
056: public String getTextMessage(String messageID) {
057: return getCompleteMessage(messageID, (Object[]) null);
058: }
059:
060: /** Get a message with default locale - one argument */
061: public String getTextMessage(String messageID, Object a1) {
062: return getCompleteMessage(messageID, new Object[] { a1 });
063: }
064:
065: /** Get a message with default locale - two arguments */
066: public String getTextMessage(String messageID, Object a1, Object a2) {
067: return getCompleteMessage(messageID, new Object[] { a1, a2 });
068: }
069:
070: /** Get a message with default locale - three arguments */
071: public String getTextMessage(String messageID, Object a1,
072: Object a2, Object a3) {
073: return getCompleteMessage(messageID,
074: new Object[] { a1, a2, a3 });
075: }
076:
077: /** Get a message with default locale - four arguments */
078: public String getTextMessage(String messageID, Object a1,
079: Object a2, Object a3, Object a4) {
080: return getCompleteMessage(messageID, new Object[] { a1, a2, a3,
081: a4 });
082: }
083:
084: /**
085: * Instance method to get the complete message, using the
086: * provided resource bundle name as specified when this
087: * instance was constructed
088: *
089: * If for some reason the message could not be found, we return a
090: * default message using the message arguments
091: */
092: public String getCompleteMessage(String messageID, Object[] args) {
093: return getCompleteMessage(messageID, resourceBundleName, args);
094: }
095:
096: /**
097: * Generic routine to get a message with any number of arguments.
098: *
099: * Looks in the provided resource bundle for the message, using the
100: * specified locale and then the US locale.
101: *
102: * @param locale
103: * The locale to use when looking for the message. If the message
104: * is not found using this locale, we attempt to find it using the
105: * US locale (our default).
106: *
107: * @param resourceBundleName
108: * The base name for the resource bundle to use.
109: *
110: * @param messageId
111: * The message identifier for this message
112: *
113: * @param arguments
114: * The arguments for the message
115: *
116: * @param composeDefault
117: * If this is true, this method will compose a default message if
118: * the message could not be found in the
119: * provided resource bundles. If it is false, this method will
120: * throw a MissingResourceException if the message could not be
121: * found.
122: *
123: * @return
124: * The message for the given message id, with arguments
125: * substituted.
126: *
127: * @throws MissingResourceException
128: * If the message could not be found and the
129: * <code>composeDefault</code> parameter was set to false.
130: */
131: public static String getCompleteMessage(Locale locale,
132: String resourceBundleName, String messageId,
133: Object[] arguments, boolean composeDefault)
134: throws MissingResourceException {
135: try {
136: return formatMessage(ResourceBundle.getBundle(
137: resourceBundleName, locale), messageId, arguments,
138: false);
139: } catch (MissingResourceException mre) {
140: // Try the US locale. Use composeDefault to indicate whether
141: // we should compose a default message or throw an exception if
142: // the message still is not found.
143: return formatMessage(ResourceBundle.getBundle(
144: resourceBundleName, US), messageId, arguments,
145: composeDefault);
146: }
147: }
148:
149: /**
150: * This is a wrapper for the getCompleteMessage workhorse routine
151: * using some obvious defaults, particularly for non-engine subsystems
152: * that only ever use the default locale.
153: *
154: * Get a message using the default locale. If the message is not found
155: * with the default locale, use the US locale. Do this both for the
156: * common bundle and the parent bundle.
157: *
158: * If the message is not found in common or in the parent resource
159: * bundle, return a default message composed of the message arguments.
160: *
161: * @param messageId
162: * The id to use to look up the message
163: *
164: * @param resourceBundleName
165: * The base name of the resource bundle to use.
166: *
167: * @param arguments
168: * The arguments to the message
169: */
170: public static String getCompleteMessage(String messageId,
171: String resourceBundleName, Object[] arguments)
172: throws MissingResourceException {
173: return getCompleteMessage(Locale.getDefault(),
174: resourceBundleName, messageId, arguments, true);
175: }
176:
177: /**
178: * Format a message given a resource bundle and a message id.
179: * <p>
180: * The arguments to the messages are passed via an object array. The objects
181: * in the array WILL be changed by this class. The caller should NOT get the
182: * object back from this array.
183: *
184: * @param bundle
185: * The resource bundle to use to look for the message
186: *
187: * @param messageId
188: * The message id to use for the message
189: *
190: * @param arguments
191: * The arguments for the message
192: *
193: * @param composeDefault
194: * Indicates whether a default message should be composed if
195: * the message can't be found in the resource bundle.
196: * <p>
197: * If composeDefault is false, this method will
198: * throw a MissingResourceException if the message could not be
199: * found.
200: * <p>
201: * If composeDefault is true, then if the message id is not found in
202: * the given bundle, this method composes and returns as helpful a
203: * message as possible in the format "UNKNOWN : [arg1], [arg2], ..."
204: */
205: public static String formatMessage(ResourceBundle bundle,
206: String messageId, Object[] arguments, boolean composeDefault) {
207:
208: String message = null;
209: String badArgsMessage = null;
210:
211: if (arguments == null)
212: arguments = new Object[0];
213:
214: if (bundle != null) {
215:
216: try {
217: message = bundle.getString(messageId);
218:
219: // Ensure that the right number of arguments are passed in.
220: if (SanityManager.DEBUG) {
221: int numExpected = countParams(message);
222: SanityManager
223: .ASSERT(
224: numExpected == arguments.length,
225: "Number of parameters expected for message id "
226: + messageId
227: + " ("
228: + numExpected
229: + ") does not match number of arguments received ("
230: + arguments.length + ")");
231: }
232:
233: try {
234: return MessageFormat.format(message, arguments);
235: } catch (IllegalArgumentException iae) {
236: if (!composeDefault || SanityManager.DEBUG)
237: throw iae;
238: } catch (NullPointerException npe) {
239: //
240: //null arguments cause a NullPointerException.
241: //This improves reporting.
242: if (!composeDefault || SanityManager.DEBUG)
243: throw npe;
244: }
245:
246: } catch (MissingResourceException mre) {
247: // caller will try and handle the last chance
248: if (!composeDefault)
249: throw mre;
250: }
251: }
252:
253: return composeDefaultMessage(
254: "UNKNOWN MESSAGE, id " + messageId, arguments);
255: }
256:
257: /**
258: * Count the number of substituation parameters in the message
259: */
260: private static int countParams(String message) {
261: boolean openFound = false;
262: int numparams = 0;
263:
264: for (int i = 0; i < message.length(); i++) {
265: char ch = message.charAt(i);
266: if (ch == '{') {
267: openFound = true;
268: }
269:
270: if (ch == '}' && openFound) {
271: numparams++;
272: openFound = false;
273: }
274: }
275:
276: return numparams;
277: }
278:
279: /**
280: * Compose a default message so that the user at least gets
281: * *something* useful rather than just a MissingResourceException,
282: * which is particularly unhelpful
283: *
284: * @param message
285: * The message to start with, which often is null
286: *
287: * @param arguments
288: * The arguments to the message.
289: */
290: public static String composeDefaultMessage(String message,
291: Object[] arguments) {
292: if (message == null) {
293: message = "UNKNOWN";
294: }
295:
296: StringBuffer sb = new StringBuffer(message);
297:
298: if (arguments == null) {
299: return sb.toString();
300: }
301:
302: sb.append(" : ");
303: int len = arguments.length;
304:
305: for (int i = 0; i < len; i++) {
306: // prepend a comma to all but the first
307: if (i > 0)
308: sb.append(", ");
309:
310: sb.append('[');
311: sb.append(i);
312: sb.append("] ");
313: if (arguments[i] == null)
314: sb.append("null");
315: else
316: sb.append(arguments[i].toString());
317: }
318:
319: return sb.toString();
320: }
321: }
|