1 package com.atlassian.plugin.parsers;
2
3 import com.atlassian.plugin.Application;
4 import com.atlassian.plugin.InstallationMode;
5 import com.atlassian.plugin.Plugin;
6 import com.google.common.collect.Maps;
7 import com.google.common.collect.Sets;
8 import org.apache.commons.lang3.StringUtils;
9 import org.dom4j.Attribute;
10 import org.dom4j.Element;
11
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Optional;
16 import java.util.Set;
17 import java.util.function.Function;
18 import java.util.function.Predicate;
19 import java.util.stream.Collectors;
20 import java.util.stream.Stream;
21
22 import static com.atlassian.plugin.parsers.PluginDescriptorReader.elements;
23 import static com.google.common.base.Preconditions.checkArgument;
24 import static com.google.common.base.Preconditions.checkNotNull;
25 import static com.google.common.collect.ImmutableSet.copyOf;
26 import static java.util.Optional.empty;
27 import static java.util.Optional.ofNullable;
28
29
30
31
32
33
34
35 public final class PluginInformationReader {
36 static final String PLUGIN_INFO = "plugin-info";
37
38
39
40 static final String DEFAULT_SCAN_FOLDER = "META-INF/atlassian";
41
42 private final Element pluginInfo;
43 private final Set<Application> applications;
44 private final int pluginsVersion;
45
46 PluginInformationReader(Element pluginInfo, Set<Application> applications, int pluginsVersion) {
47 this.pluginsVersion = pluginsVersion;
48 this.pluginInfo = pluginInfo;
49 this.applications = copyOf(checkNotNull(applications));
50 }
51
52 public Optional<String> getDescription() {
53 return getDescriptionElement().map(Element::getTextTrim);
54 }
55
56 public Optional<String> getDescriptionKey() {
57 return getDescriptionElement().map(description -> description.attributeValue("key"));
58 }
59
60 public Optional<String> getVersion() {
61 return childElement("version").map(Element::getTextTrim);
62 }
63
64 private Optional<Element> childElement(final String name) {
65 return pluginInfo == null ? empty() : ofNullable(pluginInfo.element(name));
66 }
67
68 private Stream<Element> childElements(final String name) {
69 return pluginInfo == null ? Stream.empty() : pluginInfo.elements(name).stream();
70 }
71
72 public Optional<String> getVendorName() {
73 return getVendorElement().map(vendor -> vendor.attributeValue("name"));
74 }
75
76 public Optional<String> getVendorUrl() {
77 return getVendorElement().map(vendor -> vendor.attributeValue("url"));
78 }
79
80
81
82
83 @Deprecated
84 public Optional<String> getScopeKey() {
85 final Optional<String> scopeKey = getScopeElement().map(el -> el.attributeValue("key"));
86
87 checkArgument(!scopeKey.map(String::isEmpty).orElse(false), "Value of scope key can't be blank");
88 return scopeKey;
89 }
90
91 public Map<String, String> getParameters() {
92 return Collections.unmodifiableMap(getParamElements()
93 .collect(Collectors.toMap(param -> param.attribute("name").getData().toString(), Element::getText)));
94 }
95
96 public Optional<Float> getMinVersion() {
97 return getApplicationVersionElement().flatMap(new GetAttributeFunction("min")).map(new ParseAttributeValueAsFloatFunction());
98 }
99
100 public Optional<Float> getMaxVersion() {
101 return getApplicationVersionElement().flatMap(new GetAttributeFunction("max")).map(new ParseAttributeValueAsFloatFunction());
102 }
103
104 public Optional<Float> getMinJavaVersion() {
105 return childElement("java-version").flatMap(new GetAttributeFunction("min")).map(new ParseAttributeValueAsFloatFunction());
106 }
107
108 public Map<String, Optional<String>> getPermissions() {
109 return Collections.unmodifiableMap(getPermissionElements()
110 .collect(Collectors.toMap(Element::getTextTrim,
111 perm -> ofNullable(perm.attributeValue("installation-mode")))));
112 }
113
114 public boolean hasAllPermissions() {
115 return getPermissions().isEmpty() && pluginsVersion < Plugin.VERSION_3;
116 }
117
118 public Set<String> getPermissions(final InstallationMode installationMode) {
119 return copyOf(Maps.filterValues(getPermissions(),
120 permInstallMode -> permInstallMode.flatMap(InstallationMode::of)
121 .map(installMode -> installMode.equals(installationMode))
122 .orElse(true)).keySet());
123 }
124
125 public Optional<String> getStartup() {
126 return childElement("startup").map(Element::getTextTrim);
127 }
128
129 public Iterable<String> getModuleScanFolders() {
130 final Set<String> scanFolders = Sets.newLinkedHashSet();
131 return childElement("scan-modules")
132 .map((Function<Element, Iterable<Element>>) scanModules -> {
133 List<Element> elements = elements(scanModules, "folder");
134 if (elements.isEmpty()) {
135 scanFolders.add(DEFAULT_SCAN_FOLDER);
136 }
137 return elements;
138 })
139 .map((Function<Iterable<Element>, Iterable<String>>) folders -> {
140 for (Element folder : folders) {
141 scanFolders.add(folder.getTextTrim());
142 }
143 return scanFolders;
144 })
145 .orElseGet(Collections::emptyList);
146 }
147
148 private Stream<Element> getPermissionElements() {
149 return streamOptional(childElement("permissions"))
150 .flatMap(permissions -> elements(permissions, "permission").stream())
151 .filter(new ElementWithForApplicationsPredicate(applications))
152 .filter(element -> StringUtils.isNotBlank(element.getTextTrim()));
153 }
154
155 private Optional<Element> getApplicationVersionElement() {
156 return childElement("application-version");
157 }
158
159 private Stream<Element> getParamElements() {
160 return childElements("param").filter(param -> param.attribute("name") != null);
161 }
162
163 private Optional<Element> getVendorElement() {
164 return childElement("vendor");
165 }
166
167 private Optional<Element> getScopeElement() {
168 return childElement("scope");
169 }
170
171 private Optional<Element> getDescriptionElement() {
172 return childElement("description");
173 }
174
175 private static <T> Stream<T> streamOptional(Optional<T> value) {
176 return value.map(Stream::of).orElseGet(Stream::empty);
177 }
178
179 private static final class ParseAttributeValueAsFloatFunction implements Function<Attribute, Float> {
180 @Override
181 public Float apply(Attribute attr) {
182 return Float.parseFloat(attr.getValue());
183 }
184 }
185
186 private static final class GetAttributeFunction implements Function<Element, Optional<Attribute>> {
187 private final String name;
188
189 private GetAttributeFunction(String name) {
190 this.name = name;
191 }
192
193 @Override
194 public Optional<Attribute> apply(Element applicationVersion) {
195 return ofNullable(applicationVersion.attribute(name));
196 }
197 }
198
199 private static final class ElementWithForApplicationsPredicate implements Predicate<Element> {
200 private final Set<Application> applications;
201
202 private ElementWithForApplicationsPredicate(Set<Application> applications) {
203 this.applications = checkNotNull(applications);
204 }
205
206 @Override
207 public boolean test(final Element el) {
208 final String appName = el.attributeValue("application");
209 return appName == null || applications.stream().anyMatch(app -> app != null && appName.equals(app.getKey()));
210 }
211 }
212 }