1 package com.atlassian.plugin.parsers;
2
3
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Set;
7
8 import javax.annotation.Nullable;
9
10 import com.atlassian.fugue.Option;
11 import com.atlassian.fugue.Suppliers;
12 import com.atlassian.plugin.Application;
13 import com.atlassian.plugin.InstallationMode;
14 import com.atlassian.plugin.Plugin;
15
16 import com.google.common.base.Function;
17 import com.google.common.base.Predicate;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMap;
20 import com.google.common.collect.ImmutableSet;
21 import com.google.common.collect.Iterables;
22 import com.google.common.collect.Maps;
23
24 import com.google.common.collect.Sets;
25 import org.apache.commons.lang.StringUtils;
26 import org.dom4j.Attribute;
27 import org.dom4j.Element;
28
29 import static com.atlassian.fugue.Option.option;
30 import static com.atlassian.plugin.parsers.PluginDescriptorReader.elements;
31 import static com.google.common.base.Preconditions.checkNotNull;
32 import static com.google.common.collect.ImmutableSet.copyOf;
33 import static com.google.common.collect.Iterables.filter;
34
35
36
37
38
39
40
41 public final class PluginInformationReader
42 {
43 static final String PLUGIN_INFO = "plugin-info";
44
45
46
47 static final String DEFAULT_SCAN_FOLDER = "META-INF/atlassian";
48
49 private final Option<Element> pluginInfo;
50 private final Set<Application> applications;
51 private final int pluginsVersion;
52
53 PluginInformationReader(Option<Element> pluginInfo, Set<Application> applications, int pluginsVersion)
54 {
55 this.pluginsVersion = pluginsVersion;
56 this.pluginInfo = checkNotNull(pluginInfo);
57 this.applications = copyOf(checkNotNull(applications));
58 }
59
60 public Option<String> getDescription()
61 {
62 return getDescriptionElement().map(new Function<Element, String>()
63 {
64 @Override
65 public String apply(Element description)
66 {
67 return description.getTextTrim();
68 }
69 });
70 }
71
72 public Option<String> getDescriptionKey()
73 {
74 return getDescriptionElement().flatMap(new Function<Element, Option<String>>()
75 {
76 @Override
77 public Option<String> apply(Element description)
78 {
79 return option(description.attributeValue("key"));
80 }
81 });
82 }
83
84 public Option<String> getVersion()
85 {
86 return childElement("version").map(new Function<Element, String>()
87 {
88 @Override
89 public String apply(Element version)
90 {
91 return version.getTextTrim();
92 }
93 });
94 }
95
96 private Option<Element> childElement(final String name)
97 {
98 return pluginInfo.flatMap(new ChildElementFunction(name));
99 }
100
101 private Option<Iterable<Element>> childElements(final String name)
102 {
103 return pluginInfo.map(new ChildElementsFunction(name));
104 }
105
106 public Option<String> getVendorName()
107 {
108 return getVendorElement().flatMap(new Function<Element, Option<String>>()
109 {
110 @Override
111 public Option<String> apply(Element vendor)
112 {
113 return option(vendor.attributeValue("name"));
114 }
115 });
116 }
117
118 public Option<String> getVendorUrl()
119 {
120 return getVendorElement().flatMap(new Function<Element, Option<String>>()
121 {
122 @Override
123 public Option<String> apply(Element vendor)
124 {
125 return option(vendor.attributeValue("url"));
126 }
127 });
128 }
129
130 public Map<String, String> getParameters()
131 {
132 final ImmutableMap.Builder<String, String> params = ImmutableMap.builder();
133 for (Element param : getParamElements())
134 {
135 params.put(param.attribute("name").getData().toString(), param.getText());
136 }
137 return params.build();
138 }
139
140 public Option<Float> getMinVersion()
141 {
142 return getApplicationVersionElement().flatMap(new GetAttributeFunction("min")).map(new ParseAttributeValueAsFloatFunction());
143 }
144
145 public Option<Float> getMaxVersion()
146 {
147 return getApplicationVersionElement().flatMap(new GetAttributeFunction("max")).map(new ParseAttributeValueAsFloatFunction());
148 }
149
150 public Option<Float> getMinJavaVersion()
151 {
152 return childElement("java-version").flatMap(new GetAttributeFunction("min")).map(new ParseAttributeValueAsFloatFunction());
153 }
154
155 public Map<String, Option<String>> getPermissions()
156 {
157 final ImmutableMap.Builder<String, Option<String>> permissions = ImmutableMap.builder();
158 for (Element permission : getPermissionElements())
159 {
160 permissions.put(permission.getTextTrim(), option(permission.attributeValue("installation-mode")));
161 }
162 return permissions.build();
163 }
164
165 public boolean hasAllPermissions()
166 {
167 return getPermissions().isEmpty() && pluginsVersion < Plugin.VERSION_3;
168 }
169
170 public Set<String> getPermissions(final InstallationMode installationMode)
171 {
172 return copyOf(Maps.filterValues(getPermissions(), new Predicate<Option<String>>()
173 {
174 @Override
175 public boolean apply(Option<String> input)
176 {
177 return input
178 .flatMap(new Function<String, Option<InstallationMode>>()
179 {
180 @Override
181 public Option<InstallationMode> apply(String mode)
182 {
183 return InstallationMode.of(mode);
184 }
185 })
186 .fold(Suppliers.alwaysTrue(), new Function<InstallationMode, Boolean>()
187 {
188 @Override
189 public Boolean apply(InstallationMode input)
190 {
191 return input.equals(installationMode);
192 }
193 });
194 }
195 }).keySet());
196 }
197
198 public Option<String> getStartup()
199 {
200 return childElement("startup").map(new Function<Element, String>()
201 {
202 @Override
203 public String apply(final Element element)
204 {
205 return element.getTextTrim();
206 }
207 });
208 }
209
210 public Iterable<String> getModuleScanFolders()
211 {
212 final Set<String> scanFolders = Sets.newLinkedHashSet();
213 return childElement("scan-modules")
214 .map(new Function<Element, Iterable<Element>>()
215 {
216 public Iterable<Element> apply(@Nullable Element scanModules)
217 {
218 List<Element> elements = elements(scanModules, "folder");
219 if (elements.isEmpty())
220 {
221 scanFolders.add(DEFAULT_SCAN_FOLDER);
222 }
223 return elements;
224 }
225
226 })
227 .map(new Function<Iterable<Element>, Iterable<String>>()
228 {
229 @Override
230 public Iterable<String> apply(@Nullable Iterable<Element> folders)
231 {
232 for (Element folder : folders)
233 {
234 scanFolders.add(folder.getTextTrim());
235 }
236 return scanFolders;
237 }
238 })
239 .getOrElse(ImmutableSet.<String>of());
240 }
241
242 private Iterable<Element> getPermissionElements()
243 {
244 return childElement("permissions")
245 .map(new Function<Element, Iterable<Element>>()
246 {
247 @Override
248 public Iterable<Element> apply(Element permissions)
249 {
250 return elements(permissions, "permission");
251 }
252 })
253 .map(new Function<Iterable<Element>, Iterable<Element>>()
254 {
255 @Override
256 public Iterable<Element> apply(@Nullable Iterable<Element> input)
257 {
258 return filter(input, new ElementWithForApplicationsPredicate(applications));
259 }
260 })
261 .map(new Function<Iterable<Element>, Iterable<Element>>()
262 {
263 @Override
264 public Iterable<Element> apply(@Nullable Iterable<Element> input)
265 {
266 return filter(input, new Predicate<Element>()
267 {
268 @Override
269 public boolean apply(Element p)
270 {
271 return StringUtils.isNotBlank(p.getTextTrim());
272 }
273 });
274 }
275 })
276 .getOrElse(ImmutableList.<Element>of());
277 }
278
279 private Option<Element> getApplicationVersionElement()
280 {
281 return childElement("application-version");
282 }
283
284 private Iterable<Element> getParamElements()
285 {
286 return childElements("param")
287 .map(new Function<Iterable<Element>, Iterable<Element>>()
288 {
289 @Override
290 public Iterable<Element> apply(Iterable<Element> input)
291 {
292 return filter(input, new Predicate<Element>()
293 {
294 @Override
295 public boolean apply(Element param)
296 {
297 return param.attribute("name") != null;
298 }
299 });
300 }
301 })
302 .getOrElse(ImmutableList.<Element>of());
303 }
304
305 private Option<Element> getVendorElement()
306 {
307 return childElement("vendor");
308 }
309
310 private Option<Element> getDescriptionElement()
311 {
312 return childElement("description");
313 }
314
315 private static final class ParseAttributeValueAsFloatFunction implements Function<Attribute, Float>
316 {
317 @Override
318 public Float apply(Attribute attr)
319 {
320 return Float.parseFloat(attr.getValue());
321 }
322 }
323
324 private static final class GetAttributeFunction implements Function<Element, Option<Attribute>>
325 {
326 private final String name;
327
328 private GetAttributeFunction(String name)
329 {
330 this.name = name;
331 }
332
333 @Override
334 public Option<Attribute> apply(Element applicationVersion)
335 {
336 return option(applicationVersion.attribute(name));
337 }
338 }
339
340 private static final class ApplicationWithNamePredicate implements Predicate<Application>
341 {
342 @Nullable
343 private final String name;
344
345 public ApplicationWithNamePredicate(String name)
346 {
347 this.name = name;
348 }
349
350 @Override
351 public boolean apply(Application app)
352 {
353 return app.getKey().equals(name);
354 }
355 }
356
357 private static final class ElementWithForApplicationsPredicate implements Predicate<Element>
358 {
359 private final Set<Application> applications;
360
361 private ElementWithForApplicationsPredicate(Set<Application> applications)
362 {
363 this.applications = checkNotNull(applications);
364 }
365
366 @Override
367 public boolean apply(final Element el)
368 {
369 final String appName = el.attributeValue("application");
370 return appName == null || Iterables.any(applications, new ApplicationWithNamePredicate(appName));
371 }
372 }
373
374 private static final class ChildElementFunction implements Function<Element, Option<Element>>
375 {
376 private final String name;
377
378 public ChildElementFunction(String name)
379 {
380 this.name = name;
381 }
382
383 @Override
384 public Option<Element> apply(Element el)
385 {
386 return option(el.element(name));
387 }
388 }
389
390 private static final class ChildElementsFunction implements Function<Element, Iterable<Element>>
391 {
392 private final String name;
393
394 public ChildElementsFunction(String name)
395 {
396 this.name = name;
397 }
398
399 @Override
400 public Iterable<Element> apply(Element el)
401 {
402 return elements(el, name);
403 }
404 }
405 }