001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.util.graph.selector; 020 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.Comparator; 024import java.util.TreeSet; 025 026import org.eclipse.aether.artifact.Artifact; 027import org.eclipse.aether.collection.DependencyCollectionContext; 028import org.eclipse.aether.collection.DependencySelector; 029import org.eclipse.aether.graph.Dependency; 030import org.eclipse.aether.graph.Exclusion; 031 032import static java.util.Objects.requireNonNull; 033 034/** 035 * A dependency selector that applies exclusions based on artifact coordinates. 036 * 037 * @see Dependency#getExclusions() 038 */ 039public final class ExclusionDependencySelector implements DependencySelector { 040 041 // sorted and dupe-free array, faster to iterate than LinkedHashSet 042 private final Exclusion[] exclusions; 043 044 private int hashCode; 045 046 /** 047 * Creates a new selector without any exclusions. 048 */ 049 public ExclusionDependencySelector() { 050 this.exclusions = new Exclusion[0]; 051 } 052 053 /** 054 * Creates a new selector with the specified exclusions. 055 * 056 * @param exclusions The exclusions, may be {@code null}. 057 */ 058 public ExclusionDependencySelector(Collection<Exclusion> exclusions) { 059 if (exclusions != null && !exclusions.isEmpty()) { 060 TreeSet<Exclusion> sorted = new TreeSet<>(ExclusionComparator.INSTANCE); 061 sorted.addAll(exclusions); 062 this.exclusions = sorted.toArray(new Exclusion[0]); 063 } else { 064 this.exclusions = new Exclusion[0]; 065 } 066 } 067 068 private ExclusionDependencySelector(Exclusion[] exclusions) { 069 this.exclusions = exclusions; 070 } 071 072 public boolean selectDependency(Dependency dependency) { 073 requireNonNull(dependency, "dependency cannot be null"); 074 Artifact artifact = dependency.getArtifact(); 075 for (Exclusion exclusion : exclusions) { 076 if (matches(exclusion, artifact)) { 077 return false; 078 } 079 } 080 return true; 081 } 082 083 private boolean matches(Exclusion exclusion, Artifact artifact) { 084 if (!matches(exclusion.getArtifactId(), artifact.getArtifactId())) { 085 return false; 086 } 087 if (!matches(exclusion.getGroupId(), artifact.getGroupId())) { 088 return false; 089 } 090 if (!matches(exclusion.getExtension(), artifact.getExtension())) { 091 return false; 092 } 093 return matches(exclusion.getClassifier(), artifact.getClassifier()); 094 } 095 096 private boolean matches(String pattern, String value) { 097 return "*".equals(pattern) || pattern.equals(value); 098 } 099 100 public DependencySelector deriveChildSelector(DependencyCollectionContext context) { 101 requireNonNull(context, "context cannot be null"); 102 Dependency dependency = context.getDependency(); 103 Collection<Exclusion> exclusions = (dependency != null) ? dependency.getExclusions() : null; 104 if (exclusions == null || exclusions.isEmpty()) { 105 return this; 106 } 107 108 Exclusion[] merged = this.exclusions; 109 int count = merged.length; 110 for (Exclusion exclusion : exclusions) { 111 int index = Arrays.binarySearch(merged, exclusion, ExclusionComparator.INSTANCE); 112 if (index < 0) { 113 index = -(index + 1); 114 if (count >= merged.length) { 115 Exclusion[] tmp = new Exclusion[merged.length + exclusions.size()]; 116 System.arraycopy(merged, 0, tmp, 0, index); 117 tmp[index] = exclusion; 118 System.arraycopy(merged, index, tmp, index + 1, count - index); 119 merged = tmp; 120 } else { 121 System.arraycopy(merged, index, merged, index + 1, count - index); 122 merged[index] = exclusion; 123 } 124 count++; 125 } 126 } 127 if (merged == this.exclusions) { 128 return this; 129 } 130 if (merged.length != count) { 131 Exclusion[] tmp = new Exclusion[count]; 132 System.arraycopy(merged, 0, tmp, 0, count); 133 merged = tmp; 134 } 135 136 return new ExclusionDependencySelector(merged); 137 } 138 139 @Override 140 public boolean equals(Object obj) { 141 if (this == obj) { 142 return true; 143 } else if (null == obj || !getClass().equals(obj.getClass())) { 144 return false; 145 } 146 147 ExclusionDependencySelector that = (ExclusionDependencySelector) obj; 148 return Arrays.equals(exclusions, that.exclusions); 149 } 150 151 @Override 152 public int hashCode() { 153 if (hashCode == 0) { 154 int hash = getClass().hashCode(); 155 hash = hash * 31 + Arrays.hashCode(exclusions); 156 hashCode = hash; 157 } 158 return hashCode; 159 } 160 161 @Override 162 public String toString() { 163 StringBuilder builder = 164 new StringBuilder().append(this.getClass().getSimpleName()).append('('); 165 for (int i = 0; i < this.exclusions.length; i++) { 166 builder.append(this.exclusions[i]); 167 if (i < this.exclusions.length - 1) { 168 builder.append(", "); 169 } 170 } 171 return builder.append(')').toString(); 172 } 173 174 private static class ExclusionComparator implements Comparator<Exclusion> { 175 176 static final ExclusionComparator INSTANCE = new ExclusionComparator(); 177 178 public int compare(Exclusion e1, Exclusion e2) { 179 if (e1 == null) { 180 return (e2 == null) ? 0 : 1; 181 } else if (e2 == null) { 182 return -1; 183 } 184 int rel = e1.getArtifactId().compareTo(e2.getArtifactId()); 185 if (rel == 0) { 186 rel = e1.getGroupId().compareTo(e2.getGroupId()); 187 if (rel == 0) { 188 rel = e1.getExtension().compareTo(e2.getExtension()); 189 if (rel == 0) { 190 rel = e1.getClassifier().compareTo(e2.getClassifier()); 191 } 192 } 193 } 194 return rel; 195 } 196 } 197}