001package org.eclipse.aether.graph; 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.AbstractSet; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.NoSuchElementException; 028import java.util.Set; 029 030import org.eclipse.aether.artifact.Artifact; 031 032/** 033 * A dependency to some artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return 034 * new objects rather than changing the current instance. 035 */ 036public final class Dependency 037{ 038 039 private final Artifact artifact; 040 041 private final String scope; 042 043 private final Boolean optional; 044 045 private final Set<Exclusion> exclusions; 046 047 /** 048 * Creates a mandatory dependency on the specified artifact with the given scope. 049 * 050 * @param artifact The artifact being depended on, must not be {@code null}. 051 * @param scope The scope of the dependency, may be {@code null}. 052 */ 053 public Dependency( Artifact artifact, String scope ) 054 { 055 this( artifact, scope, false ); 056 } 057 058 /** 059 * Creates a dependency on the specified artifact with the given scope. 060 * 061 * @param artifact The artifact being depended on, must not be {@code null}. 062 * @param scope The scope of the dependency, may be {@code null}. 063 * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}. 064 */ 065 public Dependency( Artifact artifact, String scope, Boolean optional ) 066 { 067 this( artifact, scope, optional, null ); 068 } 069 070 /** 071 * Creates a dependency on the specified artifact with the given scope and exclusions. 072 * 073 * @param artifact The artifact being depended on, must not be {@code null}. 074 * @param scope The scope of the dependency, may be {@code null}. 075 * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}. 076 * @param exclusions The exclusions that apply to transitive dependencies, may be {@code null} if none. 077 */ 078 public Dependency( Artifact artifact, String scope, Boolean optional, Collection<Exclusion> exclusions ) 079 { 080 this( artifact, scope, Exclusions.copy( exclusions ), optional ); 081 } 082 083 private Dependency( Artifact artifact, String scope, Set<Exclusion> exclusions, Boolean optional ) 084 { 085 // NOTE: This constructor assumes immutability of the provided exclusion collection, for internal use only 086 if ( artifact == null ) 087 { 088 throw new IllegalArgumentException( "no artifact specified for dependency" ); 089 } 090 this.artifact = artifact; 091 this.scope = ( scope != null ) ? scope : ""; 092 this.optional = optional; 093 this.exclusions = exclusions; 094 } 095 096 /** 097 * Gets the artifact being depended on. 098 * 099 * @return The artifact, never {@code null}. 100 */ 101 public Artifact getArtifact() 102 { 103 return artifact; 104 } 105 106 /** 107 * Sets the artifact being depended on. 108 * 109 * @param artifact The artifact, must not be {@code null}. 110 * @return The new dependency, never {@code null}. 111 */ 112 public Dependency setArtifact( Artifact artifact ) 113 { 114 if ( this.artifact.equals( artifact ) ) 115 { 116 return this; 117 } 118 return new Dependency( artifact, scope, exclusions, optional ); 119 } 120 121 /** 122 * Gets the scope of the dependency. The scope defines in which context this dependency is relevant. 123 * 124 * @return The scope or an empty string if not set, never {@code null}. 125 */ 126 public String getScope() 127 { 128 return scope; 129 } 130 131 /** 132 * Sets the scope of the dependency, e.g. "compile". 133 * 134 * @param scope The scope of the dependency, may be {@code null}. 135 * @return The new dependency, never {@code null}. 136 */ 137 public Dependency setScope( String scope ) 138 { 139 if ( this.scope.equals( scope ) || ( scope == null && this.scope.length() <= 0 ) ) 140 { 141 return this; 142 } 143 return new Dependency( artifact, scope, exclusions, optional ); 144 } 145 146 /** 147 * Indicates whether this dependency is optional or not. Optional dependencies can be ignored in some contexts. 148 * 149 * @return {@code true} if the dependency is (definitively) optional, {@code false} otherwise. 150 */ 151 public boolean isOptional() 152 { 153 return Boolean.TRUE.equals( optional ); 154 } 155 156 /** 157 * Gets the optional flag for the dependency. Note: Most clients will usually call {@link #isOptional()} to 158 * determine the optional flag, this method is for advanced use cases where three-valued logic is required. 159 * 160 * @return The optional flag or {@code null} if unspecified. 161 */ 162 public Boolean getOptional() 163 { 164 return optional; 165 } 166 167 /** 168 * Sets the optional flag for the dependency. 169 * 170 * @param optional {@code true} if the dependency is optional, {@code false} if the dependency is mandatory, may be 171 * {@code null} if unspecified. 172 * @return The new dependency, never {@code null}. 173 */ 174 public Dependency setOptional( Boolean optional ) 175 { 176 if ( eq( this.optional, optional ) ) 177 { 178 return this; 179 } 180 return new Dependency( artifact, scope, exclusions, optional ); 181 } 182 183 /** 184 * Gets the exclusions for this dependency. Exclusions can be used to remove transitive dependencies during 185 * resolution. 186 * 187 * @return The (read-only) exclusions, never {@code null}. 188 */ 189 public Collection<Exclusion> getExclusions() 190 { 191 return exclusions; 192 } 193 194 /** 195 * Sets the exclusions for the dependency. 196 * 197 * @param exclusions The exclusions, may be {@code null}. 198 * @return The new dependency, never {@code null}. 199 */ 200 public Dependency setExclusions( Collection<Exclusion> exclusions ) 201 { 202 if ( hasEquivalentExclusions( exclusions ) ) 203 { 204 return this; 205 } 206 return new Dependency( artifact, scope, optional, exclusions ); 207 } 208 209 private boolean hasEquivalentExclusions( Collection<Exclusion> exclusions ) 210 { 211 if ( exclusions == null || exclusions.isEmpty() ) 212 { 213 return this.exclusions.isEmpty(); 214 } 215 if ( exclusions instanceof Set ) 216 { 217 return this.exclusions.equals( exclusions ); 218 } 219 return exclusions.size() >= this.exclusions.size() && this.exclusions.containsAll( exclusions ) 220 && exclusions.containsAll( this.exclusions ); 221 } 222 223 @Override 224 public String toString() 225 { 226 return String.valueOf( getArtifact() ) + " (" + getScope() + ( isOptional() ? "?" : "" ) + ")"; 227 } 228 229 @Override 230 public boolean equals( Object obj ) 231 { 232 if ( obj == this ) 233 { 234 return true; 235 } 236 else if ( obj == null || !getClass().equals( obj.getClass() ) ) 237 { 238 return false; 239 } 240 241 Dependency that = (Dependency) obj; 242 243 return artifact.equals( that.artifact ) && scope.equals( that.scope ) && eq( optional, that.optional ) 244 && exclusions.equals( that.exclusions ); 245 } 246 247 private static <T> boolean eq( T o1, T o2 ) 248 { 249 return ( o1 != null ) ? o1.equals( o2 ) : o2 == null; 250 } 251 252 @Override 253 public int hashCode() 254 { 255 int hash = 17; 256 hash = hash * 31 + artifact.hashCode(); 257 hash = hash * 31 + scope.hashCode(); 258 hash = hash * 31 + ( optional != null ? optional.hashCode() : 0 ); 259 hash = hash * 31 + exclusions.size(); 260 return hash; 261 } 262 263 private static class Exclusions 264 extends AbstractSet<Exclusion> 265 { 266 267 private final Exclusion[] exclusions; 268 269 public static Set<Exclusion> copy( Collection<Exclusion> exclusions ) 270 { 271 if ( exclusions == null || exclusions.isEmpty() ) 272 { 273 return Collections.emptySet(); 274 } 275 return new Exclusions( exclusions ); 276 } 277 278 private Exclusions( Collection<Exclusion> exclusions ) 279 { 280 if ( exclusions.size() > 1 && !( exclusions instanceof Set ) ) 281 { 282 exclusions = new LinkedHashSet<Exclusion>( exclusions ); 283 } 284 this.exclusions = exclusions.toArray( new Exclusion[exclusions.size()] ); 285 } 286 287 @Override 288 public Iterator<Exclusion> iterator() 289 { 290 return new Iterator<Exclusion>() 291 { 292 293 private int cursor = 0; 294 295 public boolean hasNext() 296 { 297 return cursor < exclusions.length; 298 } 299 300 public Exclusion next() 301 { 302 try 303 { 304 Exclusion exclusion = exclusions[cursor]; 305 cursor++; 306 return exclusion; 307 } 308 catch ( IndexOutOfBoundsException e ) 309 { 310 throw new NoSuchElementException(); 311 } 312 } 313 314 public void remove() 315 { 316 throw new UnsupportedOperationException(); 317 } 318 319 }; 320 } 321 322 @Override 323 public int size() 324 { 325 return exclusions.length; 326 } 327 328 } 329 330}