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.plugins.dependency.exclusion;
20  
21  import java.lang.reflect.Proxy;
22  import java.nio.file.FileSystems;
23  import java.nio.file.Path;
24  import java.nio.file.PathMatcher;
25  import java.util.Objects;
26  import java.util.function.Predicate;
27  
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.Exclusion;
30  import org.apache.maven.model.InputLocation;
31  
32  /**
33   * Simple "record" to hold the coordinates of the dependency which is excluded.
34   * <p>
35   * When dealing with exclusions the version is not important. Only groupId and artifactId is used.
36   * </p>
37   */
38  class Coordinates implements Comparable<Coordinates> {
39  
40      private final String groupId;
41  
42      private final String artifactId;
43  
44      private final Dependency dependency;
45  
46      private final InputLocation location;
47  
48      private Coordinates(String groupId, String artifactId, Dependency dependency, InputLocation location) {
49          this.groupId = groupId;
50          this.artifactId = artifactId;
51          this.dependency = dependency;
52          this.location = location;
53      }
54  
55      public static Coordinates coordinates(String groupId, String artifactId) {
56          return new Coordinates(groupId, artifactId, null, null);
57      }
58  
59      public static Coordinates coordinates(Dependency dependency) {
60          return new Coordinates(dependency.getGroupId(), dependency.getArtifactId(), dependency, null);
61      }
62  
63      public static Coordinates coordinates(Exclusion exclusion) {
64          return new Coordinates(exclusion.getGroupId(), exclusion.getArtifactId(), null, exclusion.getLocation(""));
65      }
66  
67      public String getGroupId() {
68          return groupId;
69      }
70  
71      public String getArtifactId() {
72          return artifactId;
73      }
74  
75      public Dependency getDependency() {
76          return dependency;
77      }
78  
79      Predicate<Coordinates> getExclusionPattern() {
80          PathMatcher groupId = FileSystems.getDefault().getPathMatcher("glob:" + getGroupId());
81          PathMatcher artifactId = FileSystems.getDefault().getPathMatcher("glob:" + getArtifactId());
82          Predicate<Coordinates> predGroupId = a -> groupId.matches(createPathProxy(a.getGroupId()));
83          Predicate<Coordinates> predArtifactId = a -> artifactId.matches(createPathProxy(a.getArtifactId()));
84  
85          return predGroupId.and(predArtifactId);
86      }
87  
88      /**
89       * In order to reuse the glob matcher from the filesystem, we need
90       * to create Path instances.  Those are only used with the toString method.
91       * This hack works because the only system-dependent thing is the path
92       * separator which should not be part of the groupId or artifactId.
93       */
94      private static Path createPathProxy(String value) {
95          return (Path) Proxy.newProxyInstance(
96                  Coordinates.class.getClassLoader(), new Class[] {Path.class}, (proxy1, method, args) -> {
97                      if ("toString".equals(method.getName())) {
98                          return value;
99                      }
100                     throw new UnsupportedOperationException();
101                 });
102     }
103 
104     @Override
105     public boolean equals(Object o) {
106         if (this == o) {
107             return true;
108         }
109         if (o == null || getClass() != o.getClass()) {
110             return false;
111         }
112         Coordinates that = (Coordinates) o;
113         return Objects.equals(groupId, that.groupId)
114                 && Objects.equals(artifactId, that.artifactId)
115                 && Objects.equals(location, that.location);
116     }
117 
118     @Override
119     public int hashCode() {
120         return Objects.hash(groupId, artifactId, location);
121     }
122 
123     @Override
124     public int compareTo(Coordinates that) {
125 
126         if (location != null && that.location != null) {
127             return location.getLineNumber() - that.location.getLineNumber();
128         }
129 
130         return toString().compareTo(that.toString());
131     }
132 
133     @Override
134     public String toString() {
135         String version = "";
136         if (dependency != null) {
137             version = ":" + dependency.getVersion();
138         }
139         String line = "";
140         if (location != null) {
141             line = " @ line: " + location.getLineNumber();
142         }
143         return groupId + ":" + artifactId + version + line;
144     }
145 }