View Javadoc
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   * Represents a list of {@link ModuleRestrict}. This represents the restricts
26   * as used in the plugin descriptor:
27   * <code><br>
28   * &nbsp;&nbsp;&lt;module key="module-key"><br>
29   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;restrict ...>...&lt;/restrict><br>
30   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;restrict ...>...&lt;/restrict><br>
31   * &nbsp;&nbsp;&lt;/module>
32   * </code>
33   *
34   * @since 3.0
35   */
36  final class ModuleRestricts {
37      final Iterable<ModuleRestrict> restricts;
38  
39      private ModuleRestricts() {
40          this(ImmutableList.<ModuleRestrict>of());
41      }
42  
43      private ModuleRestricts(Iterable<ModuleRestrict> restricts) {
44          this.restricts = ImmutableList.copyOf(restricts);
45      }
46  
47      static ModuleRestricts parse(Element moduleElement) {
48          final String applicationKeys = moduleElement.attributeValue("application");
49          if (applicationKeys != null) {
50              return parseApplicationsFromAttribute(applicationKeys);
51          } else if (!moduleElement.elements("restrict").isEmpty()) {
52              @SuppressWarnings("unchecked")
53              final List<Element> restrict = moduleElement.elements("restrict");
54              return parseApplicationsFromRestrictElements(restrict);
55          } else {
56              return new ModuleRestricts();
57          }
58      }
59  
60      private static ModuleRestricts parseApplicationsFromRestrictElements(List<Element> restrictElements) {
61          return new ModuleRestricts(Iterables.transform(restrictElements, new Function<Element, ModuleRestrict>() {
62              @Override
63              public ModuleRestrict apply(Element restrictElement) {
64                  final String application = restrictElement.attributeValue("application");
65                  checkState(application != null, "No application defined for 'restrict' element.");
66                  return new ModuleRestrict(application, parseInstallationMode(restrictElement), parseVersionRange(restrictElement));
67              }
68          }));
69      }
70  
71      private static Option<InstallationMode> parseInstallationMode(Element restrictElement) {
72          return InstallationMode.of(restrictElement.attributeValue("mode"));
73      }
74  
75      private static VersionRange parseVersionRange(Element restrictElement) {
76          final String version = restrictElement.attributeValue("version");
77          if (version != null) {
78              return VersionRange.parse(version);
79          } else {
80              final List<Element> versionElements = restrictElement.elements("version");
81              if (!versionElements.isEmpty()) {
82                  VersionRange range = VersionRange.empty();
83                  for (Element versionElement : versionElements) {
84                      range = range.or(VersionRange.parse(versionElement.getText()));
85                  }
86                  return range;
87              } else {
88                  return VersionRange.all();
89              }
90          }
91      }
92  
93      private static ModuleRestricts parseApplicationsFromAttribute(String applicationKeys) {
94          final String[] keys = applicationKeys.split("\\s*,[,\\s]*");
95          final Iterable<ModuleRestrict> restricts = transform(filter(newArrayList(keys), new IsNotBlankPredicate()), new Function<String, ModuleRestrict>() {
96              @Override
97              public ModuleRestrict apply(String app) {
98                  return new ModuleRestrict(app);
99              }
100         });
101 
102         return new ModuleRestricts(restricts);
103     }
104 
105     public boolean isValidFor(Set<Application> applications, InstallationMode mode) {
106         if (Iterables.isEmpty(restricts)) {
107             return true;
108         }
109 
110         for (Application application : applications) {
111             if (Iterables.any(restricts, new RestrictMatchesApplication(application, mode))) {
112                 return true;
113             }
114         }
115         return false;
116     }
117 
118     @Override
119     public String toString() {
120         return restricts.toString();
121     }
122 
123     static final class ModuleRestrict {
124         final String application;
125         final Option<InstallationMode> mode;
126         final VersionRange version;
127 
128         ModuleRestrict(String application) {
129             this(application, Option.<InstallationMode>none());
130         }
131 
132         ModuleRestrict(String application, Option<InstallationMode> mode) {
133             this(application, mode, VersionRange.all());
134         }
135 
136         ModuleRestrict(String application, Option<InstallationMode> mode, VersionRange version) {
137             this.application = checkNotNull(application);
138             this.mode = checkNotNull(mode);
139             this.version = checkNotNull(version);
140         }
141 
142         @Override
143         public String toString() {
144             return Objects.toStringHelper("restrict")
145                     .add("application", application)
146                     .add("mode", mode)
147                     .add("range", version)
148                     .toString();
149         }
150     }
151 
152     private static final class IsNotBlankPredicate implements Predicate<String> {
153         @Override
154         public boolean apply(String input) {
155             return StringUtils.isNotBlank(input);
156         }
157     }
158 
159     private static final class RestrictMatchesApplication implements Predicate<ModuleRestrict> {
160         private final Application app;
161         private final InstallationMode installationMode;
162 
163         public RestrictMatchesApplication(Application app, InstallationMode installationMode) {
164             this.app = checkNotNull(app);
165             this.installationMode = checkNotNull(installationMode);
166         }
167 
168         @Override
169         public boolean apply(ModuleRestrict restrict) {
170             return restrict.application.equals(app.getKey())
171                     && isInstallModeValid(restrict.mode)
172                     && restrict.version.isInRange(app.getVersion());
173         }
174 
175         private boolean isInstallModeValid(Option<InstallationMode> mode) {
176             return mode.fold(alwaysTrue(), new Function<InstallationMode, Boolean>() {
177                 @Override
178                 public Boolean apply(InstallationMode m) {
179                     return m.equals(installationMode);
180                 }
181             });
182         }
183     }
184 }