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