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 final int hashCode; 045 046 /** 047 * Creates a new selector without any exclusions. 048 */ 049 public ExclusionDependencySelector() { 050 this.exclusions = new Exclusion[0]; 051 this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(exclusions); 052 } 053 054 /** 055 * Creates a new selector with the specified exclusions. 056 * 057 * @param exclusions the exclusions, may be {@code null} 058 */ 059 public ExclusionDependencySelector(Collection<Exclusion> exclusions) { 060 if (exclusions != null && !exclusions.isEmpty()) { 061 TreeSet<Exclusion> sorted = new TreeSet<>(ExclusionComparator.INSTANCE); 062 sorted.addAll(exclusions); 063 this.exclusions = sorted.toArray(new Exclusion[0]); 064 } else { 065 this.exclusions = new Exclusion[0]; 066 } 067 this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(this.exclusions); 068 } 069 070 private ExclusionDependencySelector(Exclusion[] exclusions) { 071 this.exclusions = exclusions; 072 this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(exclusions); 073 } 074 075 public boolean selectDependency(Dependency dependency) { 076 requireNonNull(dependency, "dependency cannot be null"); 077 Artifact artifact = dependency.getArtifact(); 078 for (Exclusion exclusion : exclusions) { 079 if (matches(exclusion, artifact)) { 080 return false; 081 } 082 } 083 return true; 084 } 085 086 private boolean matches(Exclusion exclusion, Artifact artifact) { 087 if (!matches(exclusion.getArtifactId(), artifact.getArtifactId())) { 088 return false; 089 } 090 if (!matches(exclusion.getGroupId(), artifact.getGroupId())) { 091 return false; 092 } 093 if (!matches(exclusion.getExtension(), artifact.getExtension())) { 094 return false; 095 } 096 return matches(exclusion.getClassifier(), artifact.getClassifier()); 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 return hashCode; 157 } 158 159 @Override 160 public String toString() { 161 StringBuilder builder = 162 new StringBuilder().append(this.getClass().getSimpleName()).append('('); 163 for (int i = 0; i < this.exclusions.length; i++) { 164 builder.append(this.exclusions[i]); 165 if (i < this.exclusions.length - 1) { 166 builder.append(", "); 167 } 168 } 169 return builder.append(')').toString(); 170 } 171 172 private static class ExclusionComparator implements Comparator<Exclusion> { 173 174 static final ExclusionComparator INSTANCE = new ExclusionComparator(); 175 176 public int compare(Exclusion e1, Exclusion e2) { 177 if (e1 == null) { 178 return (e2 == null) ? 0 : 1; 179 } else if (e2 == null) { 180 return -1; 181 } 182 int rel = e1.getArtifactId().compareTo(e2.getArtifactId()); 183 if (rel == 0) { 184 rel = e1.getGroupId().compareTo(e2.getGroupId()); 185 if (rel == 0) { 186 rel = e1.getExtension().compareTo(e2.getExtension()); 187 if (rel == 0) { 188 rel = e1.getClassifier().compareTo(e2.getClassifier()); 189 } 190 } 191 } 192 return rel; 193 } 194 } 195}