001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2015 ForgeRock AS. 016 */ 017 018package org.opends.server.admin; 019import org.forgerock.i18n.LocalizableMessage; 020 021 022 023import java.text.MessageFormat; 024import java.util.HashMap; 025import java.util.Locale; 026import java.util.Map; 027import java.util.MissingResourceException; 028import java.util.ResourceBundle; 029 030 031 032/** 033 * A class for retrieving internationalized resource properties 034 * associated with a managed object definition. 035 * <p> 036 * I18N resource properties are not available for the 037 * {@link TopCfgDefn}. 038 */ 039public final class ManagedObjectDefinitionI18NResource { 040 041 /** Application-wide set of instances. */ 042 private static final Map<String, ManagedObjectDefinitionI18NResource> INSTANCES = new HashMap<>(); 043 044 /** 045 * Gets the internationalized resource instance which can be used to 046 * retrieve the localized descriptions for the managed objects and 047 * their associated properties and relations. 048 * 049 * @return Returns the I18N resource instance. 050 */ 051 public static ManagedObjectDefinitionI18NResource getInstance() { 052 return getInstance("admin.messages"); 053 } 054 055 056 057 /** 058 * Gets the internationalized resource instance for the named 059 * profile. 060 * 061 * @param profile 062 * The name of the profile. 063 * @return Returns the I18N resource instance for the named profile. 064 */ 065 public static ManagedObjectDefinitionI18NResource getInstanceForProfile( 066 String profile) { 067 return getInstance("admin.profiles." + profile); 068 } 069 070 071 072 /** Get a resource instance creating it if necessary. */ 073 private static synchronized ManagedObjectDefinitionI18NResource getInstance( 074 String prefix) { 075 ManagedObjectDefinitionI18NResource instance = INSTANCES.get(prefix); 076 077 if (instance == null) { 078 instance = new ManagedObjectDefinitionI18NResource(prefix); 079 INSTANCES.put(prefix, instance); 080 } 081 082 return instance; 083 } 084 085 086 087 /** Mapping from definition to locale-based resource bundle. */ 088 private final Map<AbstractManagedObjectDefinition<?, ?>, 089 Map<Locale, ResourceBundle>> resources; 090 091 092 093 /** The resource name prefix. */ 094 private final String prefix; 095 096 097 098 /** Private constructor. */ 099 private ManagedObjectDefinitionI18NResource(String prefix) { 100 this.resources = new HashMap<>(); 101 this.prefix = prefix; 102 } 103 104 105 106 /** 107 * Get the internationalized message associated with the specified 108 * key in the default locale. 109 * 110 * @param d 111 * The managed object definition. 112 * @param key 113 * The resource key. 114 * @return Returns the internationalized message associated with the 115 * specified key in the default locale. 116 * @throws MissingResourceException 117 * If the key was not found. 118 * @throws UnsupportedOperationException 119 * If the provided managed object definition was the 120 * {@link TopCfgDefn}. 121 */ 122 public LocalizableMessage getMessage(AbstractManagedObjectDefinition<?, ?> d, String key) 123 throws MissingResourceException, UnsupportedOperationException { 124 return getMessage(d, key, Locale.getDefault(), (String[]) null); 125 } 126 127 128 129 /** 130 * Get the internationalized message associated with the specified 131 * key and locale. 132 * 133 * @param d 134 * The managed object definition. 135 * @param key 136 * The resource key. 137 * @param locale 138 * The locale. 139 * @return Returns the internationalized message associated with the 140 * specified key and locale. 141 * @throws MissingResourceException 142 * If the key was not found. 143 * @throws UnsupportedOperationException 144 * If the provided managed object definition was the 145 * {@link TopCfgDefn}. 146 */ 147 public LocalizableMessage getMessage(AbstractManagedObjectDefinition<?, ?> d, 148 String key, Locale locale) throws MissingResourceException, 149 UnsupportedOperationException { 150 return getMessage(d, key, locale, (String[]) null); 151 } 152 153 154 155 /** 156 * Get the parameterized internationalized message associated with 157 * the specified key and locale. 158 * 159 * @param d 160 * The managed object definition. 161 * @param key 162 * The resource key. 163 * @param locale 164 * The locale. 165 * @param args 166 * Arguments that should be inserted into the retrieved 167 * message. 168 * @return Returns the internationalized message associated with the 169 * specified key and locale. 170 * @throws MissingResourceException 171 * If the key was not found. 172 * @throws UnsupportedOperationException 173 * If the provided managed object definition was the 174 * {@link TopCfgDefn}. 175 */ 176 public LocalizableMessage getMessage(AbstractManagedObjectDefinition<?, ?> d, 177 String key, Locale locale, String... args) 178 throws MissingResourceException, UnsupportedOperationException { 179 ResourceBundle resource = getResourceBundle(d, locale); 180 181 // TODO: use message framework directly 182 if (args == null) { 183 return LocalizableMessage.raw(resource.getString(key)); 184 } else { 185 MessageFormat mf = new MessageFormat(resource.getString(key)); 186 return LocalizableMessage.raw(mf.format(args)); 187 } 188 } 189 190 191 192 /** 193 * Get the parameterized internationalized message associated with 194 * the specified key in the default locale. 195 * 196 * @param d 197 * The managed object definition. 198 * @param key 199 * The resource key. 200 * @param args 201 * Arguments that should be inserted into the retrieved 202 * message. 203 * @return Returns the internationalized message associated with the 204 * specified key in the default locale. 205 * @throws MissingResourceException 206 * If the key was not found. 207 * @throws UnsupportedOperationException 208 * If the provided managed object definition was the 209 * {@link TopCfgDefn}. 210 */ 211 public LocalizableMessage getMessage(AbstractManagedObjectDefinition<?, ?> d, 212 String key, String... args) throws MissingResourceException, 213 UnsupportedOperationException { 214 return getMessage(d, key, Locale.getDefault(), args); 215 } 216 217 218 219 /** 220 * Forcefully removes any resource bundles associated with the 221 * provided definition and using the default locale. 222 * <p> 223 * This method is intended for internal testing only. 224 * 225 * @param d 226 * The managed object definition. 227 */ 228 synchronized void removeResourceBundle( 229 AbstractManagedObjectDefinition<?, ?> d) { 230 removeResourceBundle(d, Locale.getDefault()); 231 } 232 233 234 235 /** 236 * Forcefully removes any resource bundles associated with the 237 * provided definition and locale. 238 * <p> 239 * This method is intended for internal testing only. 240 * 241 * @param d 242 * The managed object definition. 243 * @param locale 244 * The locale. 245 */ 246 synchronized void removeResourceBundle( 247 AbstractManagedObjectDefinition<?, ?> d, Locale locale) { 248 // Get the locale resource mapping. 249 Map<Locale, ResourceBundle> map = resources.get(d); 250 if (map != null) { 251 map.remove(locale); 252 } 253 } 254 255 256 257 /** 258 * Forcefully adds the provided resource bundle to this I18N 259 * resource for the default locale. 260 * <p> 261 * This method is intended for internal testing only. 262 * 263 * @param d 264 * The managed object definition. 265 * @param resoureBundle 266 * The resource bundle to be used. 267 */ 268 synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d, 269 ResourceBundle resoureBundle) { 270 setResourceBundle(d, Locale.getDefault(), resoureBundle); 271 } 272 273 274 275 /** 276 * Forcefully adds the provided resource bundle to this I18N 277 * resource. 278 * <p> 279 * This method is intended for internal testing only. 280 * 281 * @param d 282 * The managed object definition. 283 * @param locale 284 * The locale. 285 * @param resoureBundle 286 * The resource bundle to be used. 287 */ 288 synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d, 289 Locale locale, ResourceBundle resoureBundle) { 290 // First get the locale-resource mapping, creating it if 291 // necessary. 292 Map<Locale, ResourceBundle> map = resources.get(d); 293 if (map == null) { 294 map = new HashMap<>(); 295 resources.put(d, map); 296 } 297 298 // Add the resource bundle. 299 map.put(locale, resoureBundle); 300 } 301 302 303 304 /** 305 * Retrieve the resource bundle associated with a managed object and 306 * locale, lazily loading it if necessary. 307 */ 308 private synchronized ResourceBundle getResourceBundle( 309 AbstractManagedObjectDefinition<?, ?> d, Locale locale) 310 throws MissingResourceException, UnsupportedOperationException { 311 if (d.isTop()) { 312 throw new UnsupportedOperationException( 313 "I18n resources are not available for the " 314 + "Top configuration definition"); 315 } 316 317 // First get the locale-resource mapping, creating it if 318 // necessary. 319 Map<Locale, ResourceBundle> map = resources.get(d); 320 if (map == null) { 321 map = new HashMap<>(); 322 resources.put(d, map); 323 } 324 325 // Now get the resource based on the locale, loading it if 326 // necessary. 327 ResourceBundle resourceBundle = map.get(locale); 328 if (resourceBundle == null) { 329 String baseName = prefix + "." + d.getClass().getName(); 330 resourceBundle = ResourceBundle.getBundle(baseName, locale, 331 ClassLoaderProvider.getInstance().getClassLoader()); 332 map.put(locale, resourceBundle); 333 } 334 335 return resourceBundle; 336 } 337}