View Javadoc
1   package com.atlassian.refapp.sal.message;
2   
3   import com.atlassian.plugin.Plugin;
4   import com.atlassian.plugin.PluginAccessor;
5   import com.atlassian.plugin.elements.ResourceDescriptor;
6   import com.atlassian.plugin.event.PluginEventListener;
7   import com.atlassian.plugin.event.PluginEventManager;
8   import com.atlassian.plugin.event.events.PluginDisabledEvent;
9   import com.atlassian.plugin.event.events.PluginEnabledEvent;
10  import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
11  import com.atlassian.sal.api.message.I18nResolver;
12  import com.atlassian.sal.core.message.AbstractI18nResolver;
13  import com.atlassian.util.concurrent.ManagedLock;
14  import com.atlassian.util.concurrent.ManagedLocks;
15  import com.atlassian.util.concurrent.Supplier;
16  import org.apache.commons.lang.StringUtils;
17  
18  import javax.inject.Inject;
19  import javax.inject.Named;
20  import java.io.Serializable;
21  import java.text.MessageFormat;
22  import java.util.Enumeration;
23  import java.util.HashMap;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  import java.util.MissingResourceException;
30  import java.util.ResourceBundle;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.concurrent.locks.ReentrantReadWriteLock;
33  
34  /**
35   * Returns the key with args as a string
36   */
37  @ExportAsService(I18nResolver.class)
38  @Named("i18nResolver")
39  public class RefimplI18nResolver extends AbstractI18nResolver {
40      private ManagedLock.ReadWrite locks = ManagedLocks.manageReadWrite(new ReentrantReadWriteLock());
41      private final Map<Plugin, Iterable<String>> pluginResourceBundleNames = new ConcurrentHashMap<Plugin, Iterable<String>>();
42  
43      private final ResourceBundleResolver resolver;
44  
45      @Inject
46      public RefimplI18nResolver(final PluginAccessor pluginAccessor,
47                                 PluginEventManager pluginEventManager,
48                                 ResourceBundleResolver resolver) {
49          pluginEventManager.register(this);
50          this.resolver = assertNotNull(resolver, "resolver");
51  
52          locks.write().withLock(new Runnable() {
53              public void run() {
54                  addPluginResourceBundles(pluginAccessor.getPlugins());
55              }
56          });
57      }
58  
59      @Override
60      public String getRawText(String key) {
61          return StringUtils.defaultString(getPattern(Locale.getDefault(), key), key);
62      }
63  
64      @Override
65      public String getRawText(Locale locale, String key) {
66          return StringUtils.defaultString(getPattern(locale, key), key);
67      }
68  
69      public String resolveText(String key, Serializable[] arguments) {
70          String pattern = getPattern(Locale.getDefault(), key);
71          if (pattern == null) {
72              return key;
73          }
74          return MessageFormat.format(pattern, (Object[]) arguments);
75      }
76  
77      @Override
78      public String resolveText(final Locale locale, final String key, final Serializable[] arguments) {
79          String pattern = StringUtils.defaultString(getPattern(locale, key), getPattern(Locale.getDefault(), key));
80          if (pattern == null) {
81              return key;
82          }
83          return MessageFormat.format(pattern, (Object[]) arguments);
84      }
85  
86      private String getPattern(final Locale locale, final String key) {
87          return locks.read().withLock(new Supplier<String>() {
88              public String get() {
89                  String bundleString = null;
90                  for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
91                      for (String bundleName : pluginBundleNames.getValue()) {
92                          try {
93                              ResourceBundle bundle = getBundle(bundleName, locale, pluginBundleNames.getKey());
94                              bundleString = bundle.getString(key);
95                          } catch (MissingResourceException e) {
96                              // ignore, try next bundle
97                          }
98                      }
99                  }
100                 return bundleString;
101             }
102         });
103     }
104 
105     public Map<String, String> getAllTranslationsForPrefix(final String prefix) {
106         assertNotNull(prefix, "prefix");
107 
108         return locks.read().withLock(new Supplier<Map<String, String>>() {
109             public Map<String, String> get() {
110                 Map<String, String> translationsWithPrefix = new HashMap<String, String>();
111                 for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
112 
113                     addMatchingTranslationsToMap(
114                             prefix, Locale.getDefault(), pluginBundleNames.getKey(), pluginBundleNames.getValue(), translationsWithPrefix);
115                 }
116                 return translationsWithPrefix;
117             }
118         });
119     }
120 
121     public Map<String, String> getAllTranslationsForPrefix(final String prefix, final Locale locale) {
122         assertNotNull(prefix, "prefix");
123         assertNotNull(locale, "locale");
124 
125         return locks.read().withLock(new Supplier<Map<String, String>>() {
126             public Map<String, String> get() {
127                 Map<String, String> translationsWithPrefix = new HashMap<String, String>();
128                 for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
129                     addMatchingTranslationsToMap(
130                             prefix, locale, pluginBundleNames.getKey(), pluginBundleNames.getValue(), translationsWithPrefix);
131                 }
132                 return translationsWithPrefix;
133             }
134         });
135     }
136 
137     private void addMatchingTranslationsToMap(String prefix, Locale locale, Plugin plugin,
138                                               Iterable<String> bundleNames,
139                                               Map<String, String> translationsWithPrefix) {
140         for (String bundleName : bundleNames) {
141             try {
142                 ResourceBundle bundle = getBundle(bundleName, locale, plugin);
143                 if (bundle != null) {
144                     addMatchingTranslationsToMap(prefix, bundle, translationsWithPrefix);
145                 }
146             } catch (MissingResourceException e) {
147                 // OK, just ignore
148             }
149         }
150     }
151 
152     private void addMatchingTranslationsToMap(String prefix, ResourceBundle bundle,
153                                               Map<String, String> translationsWithPrefix) {
154         Enumeration enumeration = bundle.getKeys();
155         while (enumeration.hasMoreElements()) {
156             String key = (String) enumeration.nextElement();
157             if (key.startsWith(prefix)) {
158                 translationsWithPrefix.put(key, bundle.getString(key));
159             }
160         }
161     }
162 
163     @PluginEventListener
164     public void pluginEnabled(final PluginEnabledEvent event) {
165         locks.write().withLock(new Runnable() {
166             public void run() {
167                 addPluginResourceBundles(event.getPlugin());
168             }
169         });
170     }
171 
172     @PluginEventListener
173     public void pluginDisabled(final PluginDisabledEvent event) {
174         locks.write().withLock(new Runnable() {
175             public void run() {
176                 removePluginResourceBundles(event.getPlugin());
177             }
178         });
179     }
180 
181     private void addPluginResourceBundles(Iterable<Plugin> plugins) {
182         for (Plugin plugin : plugins) {
183             addPluginResourceBundles(plugin);
184         }
185     }
186 
187     private void addPluginResourceBundles(Plugin plugin) {
188         List<String> bundleNames = new LinkedList<String>();
189         Iterable<ResourceDescriptor> descriptors = plugin.getResourceDescriptors("i18n");
190         for (ResourceDescriptor descriptor : descriptors) {
191             bundleNames.add(descriptor.getLocation());
192         }
193         addPluginResourceBundles(plugin, bundleNames);
194     }
195 
196     private void addPluginResourceBundles(Plugin plugin, List<String> bundleNames) {
197         pluginResourceBundleNames.put(plugin, bundleNames);
198     }
199 
200     private void removePluginResourceBundles(Plugin plugin) {
201         pluginResourceBundleNames.remove(plugin);
202     }
203 
204     private ResourceBundle getBundle(String bundleName, Locale locale, Plugin plugin) {
205         return resolver.getBundle(bundleName, locale, plugin.getClassLoader());
206     }
207 
208     private static <T> T assertNotNull(T object, String name) {
209         if (object == null) {
210             throw new NullPointerException(name + " must not be null");
211         }
212         return object;
213     }
214 }