View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.enforcer.rules.dependency;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.maven.enforcer.rules.utils.ArtifactUtils;
28  import org.apache.maven.enforcer.rules.utils.ParentNodeProvider;
29  import org.apache.maven.enforcer.rules.utils.ParentsVisitor;
30  import org.eclipse.aether.artifact.Artifact;
31  import org.eclipse.aether.graph.DependencyNode;
32  import org.eclipse.aether.graph.DependencyVisitor;
33  
34  /**
35   * @author Brian Fox
36   */
37  class DependencyVersionMap implements DependencyVisitor, ParentNodeProvider {
38      private ParentsVisitor parentsVisitor;
39      private boolean uniqueVersions;
40      private final Map<String, List<DependencyNode>> idsToNode = new HashMap<>();
41  
42      DependencyVersionMap() {
43          this.parentsVisitor = new ParentsVisitor();
44      }
45  
46      public DependencyVersionMap setUniqueVersions(boolean uniqueVersions) {
47          this.uniqueVersions = uniqueVersions;
48          return this;
49      }
50  
51      @Override
52      public boolean visitEnter(DependencyNode node) {
53          addDependency(node);
54          return parentsVisitor.visitEnter(node) && !containsConflicts(node);
55      }
56  
57      @Override
58      public boolean visitLeave(DependencyNode node) {
59          return parentsVisitor.visitLeave(node);
60      }
61  
62      @Override
63      public DependencyNode getParent(DependencyNode node) {
64          return parentsVisitor.getParent(node);
65      }
66  
67      private String constructKey(DependencyNode node) {
68          return constructKey(node.getArtifact());
69      }
70  
71      private String constructKey(Artifact artifact) {
72          return artifact.getGroupId() + ":" + artifact.getArtifactId();
73      }
74  
75      public void addDependency(DependencyNode node) {
76          String key = constructKey(node);
77          idsToNode.computeIfAbsent(key, k -> new ArrayList<>()).add(node);
78      }
79  
80      private String getVersion(Artifact artifact) {
81          return uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
82      }
83  
84      private boolean containsConflicts(DependencyNode node) {
85          return containsConflicts(node.getArtifact());
86      }
87  
88      private boolean containsConflicts(Artifact artifact) {
89          return containsConflicts(idsToNode.get(constructKey(artifact)));
90      }
91  
92      private boolean containsConflicts(List<DependencyNode> nodes) {
93          String version = null;
94          for (DependencyNode node : nodes) {
95              if (version == null) {
96                  version = getVersion(node.getArtifact());
97              } else {
98                  if (version.compareTo(getVersion(node.getArtifact())) != 0) {
99                      return true;
100                 }
101             }
102         }
103         return false;
104     }
105 
106     public List<List<DependencyNode>> getConflictedVersionNumbers(List<String> includes, List<String> excludes) {
107         List<String> formattedIncludes = formatPatterns(includes);
108         List<String> formattedExcludes = formatPatterns(excludes);
109         List<List<DependencyNode>> output = new ArrayList<>();
110         for (List<DependencyNode> nodes : idsToNode.values()) {
111             List<DependencyNode> filteredNodes = nodes;
112             if (formattedIncludes != null || formattedExcludes != null) {
113                 filteredNodes = new ArrayList<>();
114                 for (DependencyNode node : nodes) {
115                     if (includeArtifact(node, formattedIncludes, formattedExcludes)) {
116                         filteredNodes.add(node);
117                     }
118                 }
119             }
120             if (containsConflicts(filteredNodes)) {
121                 output.add(filteredNodes);
122             }
123         }
124         return output;
125     }
126 
127     private static boolean includeArtifact(DependencyNode node, List<String> includes, List<String> excludes) {
128         boolean included = includes == null || includes.isEmpty();
129         if (!included) {
130             for (String pattern : includes) {
131                 if (ArtifactUtils.compareDependency(pattern, ArtifactUtils.toArtifact(node))) {
132                     included = true;
133                     break;
134                 }
135             }
136         }
137         if (!included) {
138             return false;
139         }
140         boolean excluded = false;
141         if (excludes != null) {
142             for (String pattern : excludes) {
143                 if (ArtifactUtils.compareDependency(pattern, ArtifactUtils.toArtifact(node))) {
144                     excluded = true;
145                     break;
146                 }
147             }
148         }
149         return !excluded;
150     }
151 
152     private static List<String> formatPatterns(List<String> patterns) {
153         if (patterns == null) {
154             return null;
155         }
156         List<String> formattedPatterns = new ArrayList<>();
157         for (String pattern : patterns) {
158             String[] subStrings = pattern.split(":");
159             subStrings = StringUtils.stripAll(subStrings);
160             String formattedPattern = StringUtils.join(subStrings, ":");
161             formattedPatterns.add(formattedPattern);
162         }
163         return formattedPatterns;
164     }
165 }