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