View Javadoc

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   * Represents a list of {@link ModuleRestrict}. This represents the restricts
23   * as used in the plugin descriptor:
24   * <code><br>
25   * &nbsp;&nbsp;&lt;module key="module-key"><br>
26   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;restrict ...>...&lt;/restrict><br>
27   * &nbsp;&nbsp;&nbsp;&nbsp;&lt;restrict ...>...&lt;/restrict><br>
28   * &nbsp;&nbsp;&lt;/module>
29   * </code>
30   *
31   * @since 2.14.0
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 }