1 package com.atlassian.plugin.util;
2
3 import com.atlassian.plugin.Application;
4 import com.atlassian.plugin.InstallationMode;
5 import com.google.common.base.MoreObjects;
6 import com.google.common.base.Predicate;
7 import com.google.common.collect.ImmutableList;
8 import com.google.common.collect.Iterables;
9 import org.apache.commons.lang3.StringUtils;
10 import org.dom4j.Element;
11
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.Set;
15
16 import static com.google.common.base.Preconditions.checkNotNull;
17 import static com.google.common.base.Preconditions.checkState;
18 import static com.google.common.collect.Iterables.filter;
19 import static com.google.common.collect.Iterables.transform;
20 import static com.google.common.collect.Lists.newArrayList;
21 import static java.util.Objects.requireNonNull;
22
23
24
25
26
27
28
29
30
31
32
33
34
35 final class ModuleRestricts {
36 final Iterable<ModuleRestrict> restricts;
37
38 private ModuleRestricts() {
39 this(ImmutableList.of());
40 }
41
42 private ModuleRestricts(Iterable<ModuleRestrict> restricts) {
43 this.restricts = ImmutableList.copyOf(restricts);
44 }
45
46 static ModuleRestricts parse(Element moduleElement) {
47 final String applicationKeys = moduleElement.attributeValue("application");
48 if (applicationKeys != null) {
49 return parseApplicationsFromAttribute(applicationKeys);
50 } else if (!moduleElement.elements("restrict").isEmpty()) {
51 @SuppressWarnings("unchecked")
52 final List<Element> restrict = moduleElement.elements("restrict");
53 return parseApplicationsFromRestrictElements(restrict);
54 } else {
55 return new ModuleRestricts();
56 }
57 }
58
59 private static ModuleRestricts parseApplicationsFromRestrictElements(List<Element> restrictElements) {
60 return new ModuleRestricts(Iterables.transform(restrictElements, restrictElement -> {
61 final String application = restrictElement.attributeValue("application");
62 checkState(application != null, "No application defined for 'restrict' element.");
63 return new ModuleRestrict(application, parseInstallationMode(restrictElement).orElse(null),
64 parseVersionRange(restrictElement));
65 }));
66 }
67
68 private static Optional<InstallationMode> parseInstallationMode(Element restrictElement) {
69 return InstallationMode.of(restrictElement.attributeValue("mode"));
70 }
71
72 private static VersionRange parseVersionRange(Element restrictElement) {
73 final String version = restrictElement.attributeValue("version");
74 if (version != null) {
75 return VersionRange.parse(version);
76 } else {
77 final List<Element> versionElements = restrictElement.elements("version");
78 if (!versionElements.isEmpty()) {
79 VersionRange range = VersionRange.empty();
80 for (Element versionElement : versionElements) {
81 range = range.or(VersionRange.parse(versionElement.getText()));
82 }
83 return range;
84 } else {
85 return VersionRange.all();
86 }
87 }
88 }
89
90 private static ModuleRestricts parseApplicationsFromAttribute(String applicationKeys) {
91 final String[] keys = applicationKeys.split("\\s*,[,\\s]*");
92 final Iterable<ModuleRestrict> restricts = transform(filter(newArrayList(keys),
93 new IsNotBlankPredicate()), ModuleRestrict::new);
94
95 return new ModuleRestricts(restricts);
96 }
97
98 public boolean isValidFor(Set<Application> applications, InstallationMode mode) {
99 if (Iterables.isEmpty(restricts)) {
100 return true;
101 }
102
103 for (Application application : applications) {
104 if (Iterables.any(restricts, new RestrictMatchesApplication(application, mode))) {
105 return true;
106 }
107 }
108 return false;
109 }
110
111 @Override
112 public String toString() {
113 return restricts.toString();
114 }
115
116 static final class ModuleRestrict {
117 final String application;
118 final InstallationMode mode;
119 final VersionRange version;
120
121 ModuleRestrict(String application) {
122 this(application, null);
123 }
124
125 ModuleRestrict(String application, InstallationMode mode) {
126 this(application, mode, VersionRange.all());
127 }
128
129 ModuleRestrict(String application, InstallationMode mode, VersionRange version) {
130 this.application = requireNonNull(application, "application");
131 this.mode = mode;
132 this.version = requireNonNull(version);
133 }
134
135 @Override
136 public String toString() {
137 return MoreObjects.toStringHelper("restrict")
138 .add("application", application)
139 .add("mode", mode)
140 .add("range", version)
141 .toString();
142 }
143 }
144
145 private static final class IsNotBlankPredicate implements Predicate<String> {
146 @Override
147 public boolean apply(String input) {
148 return StringUtils.isNotBlank(input);
149 }
150 }
151
152 private static final class RestrictMatchesApplication implements Predicate<ModuleRestrict> {
153 private final Application app;
154 private final InstallationMode installationMode;
155
156 public RestrictMatchesApplication(Application app, InstallationMode installationMode) {
157 this.app = checkNotNull(app);
158 this.installationMode = checkNotNull(installationMode);
159 }
160
161 @Override
162 public boolean apply(ModuleRestrict restrict) {
163 return restrict.application.equals(app.getKey())
164 && isInstallModeValid(restrict.mode)
165 && restrict.version.isInRange(app.getVersion());
166 }
167
168 private boolean isInstallModeValid(InstallationMode mode) {
169 return mode == null || mode.equals(installationMode);
170 }
171 }
172 }