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