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