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}