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 }