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