View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package org.basepom.mojo.duplicatefinder;
15  
16  import java.util.ArrayList;
17  import java.util.Arrays;
18  import java.util.Collections;
19  import java.util.HashSet;
20  import java.util.LinkedHashSet;
21  import java.util.List;
22  import java.util.Set;
23  import java.util.regex.Pattern;
24  
25  import com.google.common.collect.ImmutableList;
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
28  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
29  import org.apache.maven.model.Dependency;
30  import org.basepom.mojo.duplicatefinder.artifact.MavenCoordinates;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import static com.google.common.base.Preconditions.checkNotNull;
35  
36  /**
37   * Captures the <exceptions> section from the plugin configuration.
38   */
39  public class ConflictingDependency {
40      private static final Logger LOG = LoggerFactory.getLogger(ConflictingDependency.class);
41  
42      private final Set<MavenCoordinates> conflictingDependencies = new LinkedHashSet<>();
43      private final Set<String> classes = new HashSet<>();
44      private final Set<String> packages = new HashSet<>();
45      private final Set<String> resources = new HashSet<>();
46      private Pattern[] matchingResources = new Pattern[0];
47      private boolean currentProject = false;
48      private boolean currentProjectIncluded = false;
49  
50      // Called by maven
51      public void setConflictingDependencies(final Dependency[] conflictingDependencies) throws InvalidVersionSpecificationException {
52          for (Dependency conflictingDependency : conflictingDependencies) {
53              this.conflictingDependencies.add(new MavenCoordinates(conflictingDependency));
54          }
55      }
56  
57      // Called by maven
58      public void setResourcePatterns(final String[] resourcePatterns) {
59          this.matchingResources = new Pattern[resourcePatterns.length];
60          for (int i = 0; i < resourcePatterns.length; i++) {
61              this.matchingResources[i] = Pattern.compile(resourcePatterns[i], Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
62          }
63      }
64  
65      public String[] getClasses() {
66          return classes.toArray(new String[0]);
67      }
68  
69      // Called by maven
70      public void setClasses(final String[] classes) {
71          this.classes.addAll(Arrays.asList(classes));
72      }
73  
74      public String[] getPackages() {
75          return packages.toArray(new String[0]);
76      }
77  
78      // Called by maven
79      public void setPackages(final String[] packages) {
80          this.packages.addAll(Arrays.asList(packages));
81      }
82  
83      public String[] getResources() {
84          return resources.toArray(new String[0]);
85      }
86  
87      // Called by maven
88      public void setResources(final String[] resources) {
89          this.resources.addAll(Arrays.asList(resources));
90      }
91  
92      // Called by maven
93      public void setCurrentProject(final boolean currentProject) {
94          this.currentProject = currentProject;
95      }
96  
97      boolean hasCurrentProject() {
98          return currentProject;
99      }
100 
101     // Called by maven
102 
103     @Deprecated
104     public void setBootClasspath(final boolean bootClasspath) {
105         LOG.warn("<bootClasspath> attribute is deprecated and has no function!");
106     }
107 
108     boolean isCurrentProjectIncluded() {
109         return currentProjectIncluded;
110     }
111 
112     void addProjectMavenCoordinates(final MavenCoordinates projectMavenCoordinates) {
113         this.currentProjectIncluded = conflictingDependencies.contains(projectMavenCoordinates);
114         if (this.currentProject) {
115             // The exclusion should also look at the current project, add the project
116             // coordinates to the list of exclusions.
117             conflictingDependencies.add(projectMavenCoordinates);
118         }
119     }
120 
121     List<MavenCoordinates> getDependencies() {
122         return ImmutableList.copyOf(conflictingDependencies);
123     }
124 
125     Pattern[] getResourcePatterns() {
126         return matchingResources;
127     }
128 
129     public List<String> getDependencyNames() {
130         final List<String> result = new ArrayList<>(conflictingDependencies.size());
131 
132         for (final MavenCoordinates conflictingDependency : conflictingDependencies) {
133             result.add(conflictingDependency.toString());
134         }
135 
136         Collections.sort(result);
137         return result;
138     }
139 
140     public boolean isForArtifacts(final Set<Artifact> artifacts) throws OverConstrainedVersionException {
141         checkNotNull(artifacts, "artifacts is null");
142 
143         // An exception can contain more than the actually matching
144         // artifacts. In that case, we will check this exception if at least
145         // the number of artifacts are present.
146         if (artifacts.size() > conflictingDependencies.size()) {
147             return false;
148         }
149 
150         // Every artifact must be matched.
151         int numMatches = artifacts.size();
152 
153         for (final Artifact artifact : artifacts) {
154             for (final MavenCoordinates conflictingDependency : conflictingDependencies) {
155                 if (conflictingDependency.matches(artifact)) {
156                     if (--numMatches == 0) {
157                         return true;
158                     } else {
159                         break; // for (final MavenCoordinates...
160                     }
161                 }
162             }
163         }
164         return false;
165     }
166 
167     boolean isWildcard() {
168         return classes.isEmpty() && packages.isEmpty() && resources.isEmpty() && matchingResources.length == 0;
169     }
170 
171     public boolean containsClass(final String className) {
172         if (isWildcard()) {
173             // Nothing given --> match everything.
174             return true;
175         }
176 
177         if (classes.contains(className)) {
178             return true;
179         } else {
180             for (final String packageName : packages) {
181                 final String pkgName = packageName.endsWith(".") ? packageName : packageName + ".";
182                 if (className.startsWith(pkgName)) {
183                     return true;
184                 }
185             }
186             return false;
187         }
188     }
189 
190     public boolean containsResource(final String resource) {
191         if (isWildcard()) {
192             // Nothing given --> match everything
193             return true;
194         }
195 
196         final String resourceAsRelative = resource.startsWith("/") || resource.startsWith("\\") ? resource.substring(1) : resource;
197 
198         if (resources.contains(resourceAsRelative) ||
199                 resources.contains("/" + resourceAsRelative) ||
200                 resources.contains("\\" + resourceAsRelative)) {
201 
202             return true;
203         }
204 
205         for (Pattern matchingResource : matchingResources) {
206             if (matchingResource.matcher(resourceAsRelative).matches()) {
207                 return true;
208             }
209         }
210 
211         return false;
212     }
213 }