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