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.internal.impl.scope; 020 021import java.util.Collection; 022import java.util.Objects; 023 024import org.eclipse.aether.collection.DependencyCollectionContext; 025import org.eclipse.aether.collection.DependencySelector; 026import org.eclipse.aether.graph.Dependency; 027 028import static java.util.Objects.requireNonNull; 029 030/** 031 * A dependency selector that filters transitive dependencies based on their scope. It is configurable from which level 032 * applies, as it depend on "as project" and "as dependency" use cases. 033 * <p> 034 * <em>Important note:</em> equals/hashCode must factor in starting state, as instances of this class 035 * (potentially differentially configured) are used now in session, but are kept in a set. 036 * <p> 037 * <em>Note:</em> This filter does not assume any relationships between the scopes. 038 * In particular, the filter is not aware of scopes that logically include other scopes. 039 * 040 * @see Dependency#getScope() 041 */ 042public final class ScopeDependencySelector implements DependencySelector { 043 /** 044 * This enables "legacy" mode (proper): in Resolver 1.x "transitive" state depended on the presence or 045 * absence of the root node. 046 */ 047 public static ScopeDependencySelector legacy(Collection<String> included, Collection<String> excluded) { 048 return new ScopeDependencySelector( 049 Objects.hash(true, 0, 1, Integer.MAX_VALUE, included, excluded), 050 true, 051 0, 052 1, 053 Integer.MAX_VALUE, 054 included, 055 excluded); 056 } 057 058 /** 059 * Selects dependencies by scope always (from root). 060 */ 061 public static ScopeDependencySelector fromRoot(Collection<String> included, Collection<String> excluded) { 062 return from(1, included, excluded); 063 } 064 065 /** 066 * Selects dependencies by scope starting from direct dependencies. 067 */ 068 public static ScopeDependencySelector fromDirect(Collection<String> included, Collection<String> excluded) { 069 return from(2, included, excluded); 070 } 071 072 /** 073 * Selects dependencies by scope starting from given depth (1=root, 2=direct, 3=transitives of direct ones...). 074 */ 075 public static ScopeDependencySelector from( 076 int applyFrom, Collection<String> included, Collection<String> excluded) { 077 return fromTo(applyFrom, Integer.MAX_VALUE, included, excluded); 078 } 079 080 /** 081 * Selects dependencies by scope starting from given depth (1=root, 2=direct, 3=transitives of direct ones...) to 082 * given depth. 083 */ 084 public static ScopeDependencySelector fromTo( 085 int applyFrom, int applyTo, Collection<String> included, Collection<String> excluded) { 086 if (applyFrom < 1) { 087 throw new IllegalArgumentException("applyFrom must be non-zero and positive"); 088 } 089 if (applyFrom > applyTo) { 090 throw new IllegalArgumentException("applyTo must be greater or equal than applyFrom"); 091 } 092 return new ScopeDependencySelector( 093 Objects.hash(false, 0, applyFrom, applyTo, included, excluded), 094 false, 095 0, 096 applyFrom, 097 applyTo, 098 included, 099 excluded); 100 } 101 102 private final int seed; 103 private final boolean shiftIfRootNull; 104 private final int depth; 105 private final int applyFrom; 106 private final int applyTo; 107 private final Collection<String> included; 108 private final Collection<String> excluded; 109 110 private ScopeDependencySelector( 111 int seed, 112 boolean shiftIfRootNull, 113 int depth, 114 int applyFrom, 115 int applyTo, 116 Collection<String> included, 117 Collection<String> excluded) { 118 this.seed = seed; 119 this.shiftIfRootNull = shiftIfRootNull; 120 this.depth = depth; 121 this.applyFrom = applyFrom; 122 this.applyTo = applyTo; 123 this.included = included; 124 this.excluded = excluded; 125 } 126 127 @Override 128 public boolean selectDependency(Dependency dependency) { 129 requireNonNull(dependency, "dependency cannot be null"); 130 if (depth < applyFrom || depth > applyTo) { 131 return true; 132 } 133 134 String scope = dependency.getScope(); 135 return (included == null || included.contains(scope)) && (excluded == null || !excluded.contains(scope)); 136 } 137 138 @Override 139 public DependencySelector deriveChildSelector(DependencyCollectionContext context) { 140 requireNonNull(context, "context cannot be null"); 141 if (depth == 0 && shiftIfRootNull && context.getDependency() == null) { 142 return new ScopeDependencySelector( 143 seed, shiftIfRootNull, depth + 1, applyFrom + 1, applyTo, included, excluded); 144 } else { 145 return new ScopeDependencySelector( 146 seed, shiftIfRootNull, depth + 1, applyFrom, applyTo, included, excluded); 147 } 148 } 149 150 @Override 151 public boolean equals(Object obj) { 152 if (this == obj) { 153 return true; 154 } else if (null == obj || !getClass().equals(obj.getClass())) { 155 return false; 156 } 157 158 ScopeDependencySelector that = (ScopeDependencySelector) obj; 159 return seed == that.seed 160 && shiftIfRootNull == that.shiftIfRootNull 161 && depth == that.depth 162 && applyFrom == that.applyFrom 163 && applyTo == that.applyTo 164 && Objects.equals(included, that.included) 165 && Objects.equals(excluded, that.excluded); 166 } 167 168 @Override 169 public int hashCode() { 170 int hash = 17; 171 hash = hash * 31 + seed; 172 hash = hash * 31 + (shiftIfRootNull ? 0 : 1); 173 hash = hash * 31 + depth; 174 hash = hash * 31 + applyFrom; 175 hash = hash * 31 + applyTo; 176 hash = hash * 31 + (included != null ? included.hashCode() : 0); 177 hash = hash * 31 + (excluded != null ? excluded.hashCode() : 0); 178 return hash; 179 } 180 181 @Override 182 public String toString() { 183 return String.format( 184 "%s(included: %s, excluded: %s, applied: %s)", 185 getClass().getSimpleName(), included, excluded, depth >= applyFrom); 186 } 187}