1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Sep 2017 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 22 package com.sun.org.apache.xml.internal.serializer.utils; 23 24 import java.util.Locale; 25 import java.util.ResourceBundle; 26 import jdk.xml.internal.SecuritySupport; 27 28 /** 29 * A utility class for issuing error messages. 30 * 31 * A user of this class normally would create a singleton 32 * instance of this class, passing the name 33 * of the message class on the constructor. For example: 34 * <CODE> 35 * static Messages x = new Messages("org.package.MyMessages"); 36 * </CODE> 37 * Later the message is typically generated this way if there are no 38 * substitution arguments: 39 * <CODE> 40 * String msg = x.createMessage(org.package.MyMessages.KEY_ONE, null); 41 * </CODE> 42 * If there are arguments substitutions then something like this: 43 * <CODE> 44 * String filename = ...; 45 * String directory = ...; 46 * String msg = x.createMessage(org.package.MyMessages.KEY_TWO, 47 * new Object[] {filename, directory) ); 48 * </CODE> 49 * 50 * The constructor of an instance of this class must be given 51 * the class name of a class that extends java.util.ListResourceBundle 52 * ("org.package.MyMessages" in the example above). 53 * The name should not have any language suffix 54 * which will be added automatically by this utility class. 55 * 56 * The message class ("org.package.MyMessages") 57 * must define the abstract method getContents() that is 58 * declared in its base class, for example: 59 * <CODE> 60 * public Object[][] getContents() {return contents;} 61 * </CODE> 62 * 63 * It is suggested that the message class expose its 64 * message keys like this: 65 * <CODE> 66 * public static final String KEY_ONE = "KEY1"; 67 * public static final String KEY_TWO = "KEY2"; 68 * . . . 69 * </CODE> 70 * and used through their names (KEY_ONE ...) rather than 71 * their values ("KEY1" ...). 72 * 73 * The field contents (returned by getContents() 74 * should be initialized something like this: 75 * <CODE> 76 * public static final Object[][] contents = { 77 * { KEY_ONE, "Something has gone wrong!" }, 78 * { KEY_TWO, "The file ''{0}'' does not exist in directory ''{1}''." }, 79 * . . . 80 * { KEY_N, "Message N" } } 81 * </CODE> 82 * 83 * Where that section of code with the KEY to Message mappings 84 * (where the message classes 'contents' field is initialized) 85 * can have the Message strings translated in an alternate language 86 * in a errorResourceClass with a language suffix. 87 * 88 * 89 * This class is not a public API, it is only public because it is 90 * used in com.sun.org.apache.xml.internal.serializer. 91 * 92 * @xsl.usage internal 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 */ 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 */ 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 */ 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 */ 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 }