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 if (!matches(exclusion.getClassifier(), artifact.getClassifier())) { 094 return false; 095 } 096 return true; 097 } 098 099 private boolean matches(String pattern, String value) { 100 return "*".equals(pattern) || pattern.equals(value); 101 } 102 103 public DependencySelector deriveChildSelector(DependencyCollectionContext context) { 104 requireNonNull(context, "context cannot be null"); 105 Dependency dependency = context.getDependency(); 106 Collection<Exclusion> exclusions = (dependency != null) ? dependency.getExclusions() : null; 107 if (exclusions == null || exclusions.isEmpty()) { 108 return this; 109 } 110 111 Exclusion[] merged = this.exclusions; 112 int count = merged.length; 113 for (Exclusion exclusion : exclusions) { 114 int index = Arrays.binarySearch(merged, exclusion, ExclusionComparator.INSTANCE); 115 if (index < 0) { 116 index = -(index + 1); 117 if (count >= merged.length) { 118 Exclusion[] tmp = new Exclusion[merged.length + exclusions.size()]; 119 System.arraycopy(merged, 0, tmp, 0, index); 120 tmp[index] = exclusion; 121 System.arraycopy(merged, index, tmp, index + 1, count - index); 122 merged = tmp; 123 } else { 124 System.arraycopy(merged, index, merged, index + 1, count - index); 125 merged[index] = exclusion; 126 } 127 count++; 128 } 129 } 130 if (merged == this.exclusions) { 131 return this; 132 } 133 if (merged.length != count) { 134 Exclusion[] tmp = new Exclusion[count]; 135 System.arraycopy(merged, 0, tmp, 0, count); 136 merged = tmp; 137 } 138 139 return new ExclusionDependencySelector(merged); 140 } 141 142 @Override 143 public boolean equals(Object obj) { 144 if (this == obj) { 145 return true; 146 } else if (null == obj || !getClass().equals(obj.getClass())) { 147 return false; 148 } 149 150 ExclusionDependencySelector that = (ExclusionDependencySelector) obj; 151 return Arrays.equals(exclusions, that.exclusions); 152 } 153 154 @Override 155 public int hashCode() { 156 if (hashCode == 0) { 157 int hash = getClass().hashCode(); 158 hash = hash * 31 + Arrays.hashCode(exclusions); 159 hashCode = hash; 160 } 161 return hashCode; 162 } 163 164 @Override 165 public String toString() { 166 StringBuilder builder = 167 new StringBuilder().append(this.getClass().getSimpleName()).append('('); 168 for (int i = 0; i < this.exclusions.length; i++) { 169 builder.append(this.exclusions[i]); 170 if (i < this.exclusions.length - 1) { 171 builder.append(", "); 172 } 173 } 174 return builder.append(')').toString(); 175 } 176 177 private static class ExclusionComparator implements Comparator<Exclusion> { 178 179 static final ExclusionComparator INSTANCE = new ExclusionComparator(); 180 181 public int compare(Exclusion e1, Exclusion e2) { 182 if (e1 == null) { 183 return (e2 == null) ? 0 : 1; 184 } else if (e2 == null) { 185 return -1; 186 } 187 int rel = e1.getArtifactId().compareTo(e2.getArtifactId()); 188 if (rel == 0) { 189 rel = e1.getGroupId().compareTo(e2.getGroupId()); 190 if (rel == 0) { 191 rel = e1.getExtension().compareTo(e2.getExtension()); 192 if (rel == 0) { 193 rel = e1.getClassifier().compareTo(e2.getClassifier()); 194 } 195 } 196 } 197 return rel; 198 } 199 } 200}