1 package com.atlassian.plugin.refimpl.saldeps;
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.sal.api.message.I18nResolver;
11 import com.atlassian.sal.api.message.Message;
12 import com.atlassian.sal.api.message.MessageCollection;
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 java.io.Serializable;
19 import java.text.MessageFormat;
20 import java.util.Enumeration;
21 import java.util.HashMap;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.MissingResourceException;
28 import java.util.ResourceBundle;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.locks.ReentrantReadWriteLock;
31
32 import static com.google.common.base.Preconditions.checkNotNull;
33
34
35
36
37
38
39
40
41 public class CoreRefimplI18nResolver implements I18nResolver {
42 private ManagedLock.ReadWrite locks = ManagedLocks.manageReadWrite(new ReentrantReadWriteLock());
43 private final Map<Plugin, Iterable<String>> pluginResourceBundleNames = new ConcurrentHashMap<Plugin, Iterable<String>>();
44
45 public CoreRefimplI18nResolver(final PluginAccessor pluginAccessor,
46 PluginEventManager pluginEventManager) {
47 pluginEventManager.register(this);
48
49 locks.write().withLock(new Runnable() {
50 public void run() {
51 addPluginResourceBundles(pluginAccessor.getPlugins());
52 }
53 });
54 }
55
56 private static final Serializable[] EMPTY_SERIALIZABLE = new Serializable[0];
57
58 public String getText(String key, Serializable... arguments) {
59 Serializable[] resolvedArguments = new Serializable[arguments.length];
60 for (int i = 0; i < arguments.length; i++) {
61 Serializable argument = arguments[i];
62 if (argument instanceof Message) {
63 resolvedArguments[i] = getText((Message) argument);
64 } else {
65 resolvedArguments[i] = arguments[i];
66 }
67 }
68 return resolveText(key, resolvedArguments);
69 }
70
71 public String getText(Locale locale, String key, Serializable... arguments) {
72 checkNotNull(locale);
73 Serializable[] resolvedArguments = new Serializable[arguments.length];
74 for (int i = 0; i < arguments.length; i++) {
75 Serializable argument = arguments[i];
76 if (argument instanceof Message) {
77 resolvedArguments[i] = getText(locale, (Message) argument);
78 } else {
79 resolvedArguments[i] = arguments[i];
80 }
81 }
82 return resolveText(locale, key, resolvedArguments);
83 }
84
85 public String getText(String key) {
86 return resolveText(key, EMPTY_SERIALIZABLE);
87 }
88
89 public String getText(Locale locale, String key) {
90 checkNotNull(locale);
91 return resolveText(locale, key, EMPTY_SERIALIZABLE);
92 }
93
94 public String getText(Message message) {
95 return getText(message.getKey(), message.getArguments());
96 }
97
98 public String getText(Locale locale, Message message) {
99 return getText(locale, message.getKey(), message.getArguments());
100 }
101
102 public Message createMessage(String key, Serializable... arguments) {
103 return new DefaultMessage(key, arguments);
104 }
105
106 public MessageCollection createMessageCollection() {
107 return new DefaultMessageCollection();
108 }
109
110 @Override
111 public String getRawText(String key) {
112 return StringUtils.defaultString(getPattern(Locale.getDefault(), key), key);
113 }
114
115 @Override
116 public String getRawText(Locale locale, String key) {
117 return StringUtils.defaultString(getPattern(locale, key), key);
118 }
119
120 public String resolveText(String key, Serializable[] arguments) {
121 String pattern = getPattern(Locale.getDefault(), key);
122 if (pattern == null) {
123 return key;
124 }
125 return MessageFormat.format(pattern, (Object[]) arguments);
126 }
127
128 public String resolveText(final Locale locale, final String key, final Serializable[] arguments) {
129 String pattern = StringUtils.defaultString(getPattern(locale, key), getPattern(Locale.getDefault(), key));
130 if (pattern == null) {
131 return key;
132 }
133 return MessageFormat.format(pattern, (Object[]) arguments);
134 }
135
136 private String getPattern(final Locale locale, final String key) {
137 return locks.read().withLock(new Supplier<String>() {
138 public String get() {
139 String bundleString = null;
140 for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
141 for (String bundleName : pluginBundleNames.getValue()) {
142 try {
143 ResourceBundle bundle = getBundle(bundleName, locale, pluginBundleNames.getKey());
144 bundleString = bundle.getString(key);
145 } catch (MissingResourceException e) {
146
147 }
148 }
149 }
150 return bundleString;
151 }
152 });
153 }
154
155 public Map<String, String> getAllTranslationsForPrefix(final String prefix) {
156 assertNotNull(prefix, "prefix");
157
158 return locks.read().withLock(new Supplier<Map<String, String>>() {
159 public Map<String, String> get() {
160 Map<String, String> translationsWithPrefix = new HashMap<String, String>();
161 for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
162
163 addMatchingTranslationsToMap(
164 prefix, Locale.getDefault(), pluginBundleNames.getKey(), pluginBundleNames.getValue(), translationsWithPrefix);
165 }
166 return translationsWithPrefix;
167 }
168 });
169 }
170
171 public Map<String, String> getAllTranslationsForPrefix(final String prefix, final Locale locale) {
172 assertNotNull(prefix, "prefix");
173 assertNotNull(locale, "locale");
174
175 return locks.read().withLock(new Supplier<Map<String, String>>() {
176 public Map<String, String> get() {
177 Map<String, String> translationsWithPrefix = new HashMap<String, String>();
178 for (Entry<Plugin, Iterable<String>> pluginBundleNames : pluginResourceBundleNames.entrySet()) {
179 addMatchingTranslationsToMap(
180 prefix, locale, pluginBundleNames.getKey(), pluginBundleNames.getValue(), translationsWithPrefix);
181 }
182 return translationsWithPrefix;
183 }
184 });
185 }
186
187 private void addMatchingTranslationsToMap(String prefix, Locale locale, Plugin plugin,
188 Iterable<String> bundleNames,
189 Map<String, String> translationsWithPrefix) {
190 for (String bundleName : bundleNames) {
191 try {
192 ResourceBundle bundle = getBundle(bundleName, locale, plugin);
193 if (bundle != null) {
194 addMatchingTranslationsToMap(prefix, bundle, translationsWithPrefix);
195 }
196 } catch (MissingResourceException e) {
197
198 }
199 }
200 }
201
202 private void addMatchingTranslationsToMap(String prefix, ResourceBundle bundle,
203 Map<String, String> translationsWithPrefix) {
204 Enumeration enumeration = bundle.getKeys();
205 while (enumeration.hasMoreElements()) {
206 String key = (String) enumeration.nextElement();
207 if (key.startsWith(prefix)) {
208 translationsWithPrefix.put(key, bundle.getString(key));
209 }
210 }
211 }
212
213 @PluginEventListener
214 public void pluginEnabled(final PluginEnabledEvent event) {
215 locks.write().withLock(new Runnable() {
216 public void run() {
217 addPluginResourceBundles(event.getPlugin());
218 }
219 });
220 }
221
222 @PluginEventListener
223 public void pluginDisabled(final PluginDisabledEvent event) {
224 locks.write().withLock(new Runnable() {
225 public void run() {
226 removePluginResourceBundles(event.getPlugin());
227 }
228 });
229 }
230
231 private void addPluginResourceBundles(Iterable<Plugin> plugins) {
232 for (Plugin plugin : plugins) {
233 addPluginResourceBundles(plugin);
234 }
235 }
236
237 private void addPluginResourceBundles(Plugin plugin) {
238 List<String> bundleNames = new LinkedList<String>();
239 Iterable<ResourceDescriptor> descriptors = plugin.getResourceDescriptors("i18n");
240 for (ResourceDescriptor descriptor : descriptors) {
241 bundleNames.add(descriptor.getLocation());
242 }
243 addPluginResourceBundles(plugin, bundleNames);
244 }
245
246 private void addPluginResourceBundles(Plugin plugin, List<String> bundleNames) {
247 pluginResourceBundleNames.put(plugin, bundleNames);
248 }
249
250 private void removePluginResourceBundles(Plugin plugin) {
251 pluginResourceBundleNames.remove(plugin);
252 }
253
254 private ResourceBundle getBundle(String bundleName, Locale locale, Plugin plugin) {
255 return ResourceBundle.getBundle(bundleName, locale, plugin.getClassLoader());
256 }
257
258 private static <T> T assertNotNull(T object, String name) {
259 if (object == null) {
260 throw new NullPointerException(name + " must not be null");
261 }
262 return object;
263 }
264 }