1/* 2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.javac.util; 27 28import com.sun.tools.javac.api.Messages; 29import java.lang.ref.SoftReference; 30import java.util.ResourceBundle; 31import java.util.MissingResourceException; 32import java.text.MessageFormat; 33import java.util.HashMap; 34import java.util.Locale; 35import java.util.Map; 36 37/** 38 * Support for formatted localized messages. 39 * 40 * <p><b>This is NOT part of any supported API. 41 * If you write code that depends on this, you do so at your own risk. 42 * This code and its internal interfaces are subject to change or 43 * deletion without notice.</b> 44 */ 45public class JavacMessages implements Messages { 46 /** The context key for the JavacMessages object. */ 47 public static final Context.Key<JavacMessages> messagesKey = new Context.Key<>(); 48 49 /** Get the JavacMessages instance for this context. */ 50 public static JavacMessages instance(Context context) { 51 JavacMessages instance = context.get(messagesKey); 52 if (instance == null) 53 instance = new JavacMessages(context); 54 return instance; 55 } 56 57 private Map<Locale, SoftReference<List<ResourceBundle>>> bundleCache; 58 59 private List<ResourceBundleHelper> bundleHelpers; 60 61 private Locale currentLocale; 62 private List<ResourceBundle> currentBundles; 63 64 public Locale getCurrentLocale() { 65 return currentLocale; 66 } 67 68 public void setCurrentLocale(Locale locale) { 69 if (locale == null) { 70 locale = Locale.getDefault(); 71 } 72 this.currentBundles = getBundles(locale); 73 this.currentLocale = locale; 74 } 75 76 /** Creates a JavacMessages object. 77 */ 78 public JavacMessages(Context context) { 79 this(defaultBundleName, context.get(Locale.class)); 80 context.put(messagesKey, this); 81 } 82 83 /** Creates a JavacMessages object. 84 * @param bundleName the name to identify the resource bundle of localized messages. 85 */ 86 public JavacMessages(String bundleName) throws MissingResourceException { 87 this(bundleName, null); 88 } 89 90 /** Creates a JavacMessages object. 91 * @param bundleName the name to identify the resource bundle of localized messages. 92 */ 93 public JavacMessages(String bundleName, Locale locale) throws MissingResourceException { 94 bundleHelpers = List.nil(); 95 bundleCache = new HashMap<>(); 96 add(bundleName); 97 setCurrentLocale(locale); 98 } 99 100 public JavacMessages() throws MissingResourceException { 101 this(defaultBundleName); 102 } 103 104 @Override 105 public void add(String bundleName) throws MissingResourceException { 106 add(locale -> ResourceBundle.getBundle(bundleName, locale)); 107 } 108 109 public void add(ResourceBundleHelper ma) { 110 bundleHelpers = bundleHelpers.prepend(ma); 111 if (!bundleCache.isEmpty()) 112 bundleCache.clear(); 113 currentBundles = null; 114 } 115 116 public List<ResourceBundle> getBundles(Locale locale) { 117 if (locale == currentLocale && currentBundles != null) 118 return currentBundles; 119 SoftReference<List<ResourceBundle>> bundles = bundleCache.get(locale); 120 List<ResourceBundle> bundleList = bundles == null ? null : bundles.get(); 121 if (bundleList == null) { 122 bundleList = List.nil(); 123 for (ResourceBundleHelper helper : bundleHelpers) { 124 try { 125 ResourceBundle rb = helper.getResourceBundle(locale); 126 bundleList = bundleList.prepend(rb); 127 } catch (MissingResourceException e) { 128 throw new InternalError("Cannot find requested resource bundle for locale " + 129 locale, e); 130 } 131 } 132 bundleCache.put(locale, new SoftReference<>(bundleList)); 133 } 134 return bundleList; 135 } 136 137 /** Gets the localized string corresponding to a key, formatted with a set of args. 138 */ 139 public String getLocalizedString(String key, Object... args) { 140 return getLocalizedString(currentLocale, key, args); 141 } 142 143 @Override 144 public String getLocalizedString(Locale l, String key, Object... args) { 145 if (l == null) 146 l = getCurrentLocale(); 147 return getLocalizedString(getBundles(l), key, args); 148 } 149 150 /* Static access: 151 * javac has a firmly entrenched notion of a default message bundle 152 * which it can access from any static context. This is used to get 153 * easy access to simple localized strings. 154 */ 155 156 private static final String defaultBundleName = "com.sun.tools.javac.resources.compiler"; 157 private static ResourceBundle defaultBundle; 158 private static JavacMessages defaultMessages; 159 160 161 /** 162 * Returns a localized string from the compiler's default bundle. 163 */ 164 // used to support legacy Log.getLocalizedString 165 static String getDefaultLocalizedString(String key, Object... args) { 166 return getLocalizedString(List.of(getDefaultBundle()), key, args); 167 } 168 169 // used to support legacy static Diagnostic.fragment 170 @Deprecated 171 static JavacMessages getDefaultMessages() { 172 if (defaultMessages == null) 173 defaultMessages = new JavacMessages(defaultBundleName); 174 return defaultMessages; 175 } 176 177 public static ResourceBundle getDefaultBundle() { 178 try { 179 if (defaultBundle == null) 180 defaultBundle = ResourceBundle.getBundle(defaultBundleName); 181 return defaultBundle; 182 } 183 catch (MissingResourceException e) { 184 throw new Error("Fatal: Resource for compiler is missing", e); 185 } 186 } 187 188 private static String getLocalizedString(List<ResourceBundle> bundles, 189 String key, 190 Object... args) { 191 String msg = null; 192 for (List<ResourceBundle> l = bundles; l.nonEmpty() && msg == null; l = l.tail) { 193 ResourceBundle rb = l.head; 194 try { 195 msg = rb.getString(key); 196 } 197 catch (MissingResourceException e) { 198 // ignore, try other bundles in list 199 } 200 } 201 if (msg == null) { 202 msg = "compiler message file broken: key=" + key + 203 " arguments={0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}"; 204 } 205 return MessageFormat.format(msg, args); 206 } 207 208 /** 209 * This provides a way for the JavacMessager to retrieve a 210 * ResourceBundle from another module such as jdk.javadoc. 211 */ 212 public interface ResourceBundleHelper { 213 /** 214 * Gets the ResourceBundle. 215 * @param locale the requested bundle's locale 216 * @return ResourceBundle 217 */ 218 ResourceBundle getResourceBundle(Locale locale); 219 } 220} 221