View Javadoc

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   * Reads plugin information from a plugin descriptor.
37   *
38   * @see com.atlassian.plugin.parsers.PluginDescriptorReader#getPluginInformationReader()
39   * @since 3.0.0
40   */
41  public final class PluginInformationReader
42  {
43      static final String PLUGIN_INFO = "plugin-info";
44      /**
45       * The default scan-folder
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); // name might be null
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 }