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.Keys; 025import org.eclipse.aether.collection.DependencyCollectionContext; 026import org.eclipse.aether.collection.DependencySelector; 027import org.eclipse.aether.graph.Dependency; 028import org.eclipse.aether.util.artifact.ArtifactIdUtils; 029 030import static java.util.Objects.requireNonNull; 031 032/** 033 * A dependency selector that excludes optional dependencies which occur beyond given level. 034 * <p> 035 * <em>Important note:</em> equals/hashCode must factor in starting state, as instances of this class 036 * (potentially differentially configured) are used now in session, but are kept in a set. 037 * 038 * @see Dependency#isOptional() 039 */ 040public final class OptionalDependencySelector implements DependencySelector { 041 public static final Object IGNORED_KEYS = Keys.of(OptionalDependencySelector.class, "ignored"); 042 public static final Object UNSELECTED_KEYS = Keys.of(OptionalDependencySelector.class, "unselected"); 043 044 /** 045 * Excludes optional dependencies always (from root). 046 */ 047 public static OptionalDependencySelector fromRoot() { 048 return from(1); 049 } 050 051 /** 052 * Excludes optional transitive dependencies of direct dependencies. 053 */ 054 public static OptionalDependencySelector fromDirect() { 055 return from(2); 056 } 057 058 /** 059 * Excludes optional transitive dependencies from given depth (1=root, 2=direct, 3=transitives of direct ones...). 060 */ 061 public static OptionalDependencySelector from(int applyFrom) { 062 if (applyFrom < 1) { 063 throw new IllegalArgumentException("applyFrom must be non-zero and positive"); 064 } 065 return new OptionalDependencySelector(Objects.hash(applyFrom), 0, applyFrom, null, null); 066 } 067 068 private final int seed; 069 private final int depth; 070 private final int applyFrom; 071 private final Collection<String> ignoredKeys; 072 private final Collection<String> unselectedKeys; 073 074 private OptionalDependencySelector( 075 int seed, int depth, int applyFrom, Collection<String> ignoredKeys, Collection<String> unselectedKeys) { 076 this.seed = seed; 077 this.depth = depth; 078 this.applyFrom = applyFrom; 079 this.ignoredKeys = ignoredKeys; // nullable 080 this.unselectedKeys = unselectedKeys; // nullable 081 } 082 083 @Override 084 public boolean selectDependency(Dependency dependency) { 085 requireNonNull(dependency, "dependency cannot be null"); 086 String key = null; 087 if (ignoredKeys != null || unselectedKeys != null) { 088 key = ArtifactIdUtils.toId(dependency.getArtifact()); 089 } 090 if (ignoredKeys != null) { 091 if (ignoredKeys.contains(key)) { 092 return true; 093 } 094 } 095 boolean result = depth < applyFrom || !dependency.isOptional(); 096 if (!result && unselectedKeys != null) { 097 unselectedKeys.add(key); 098 } 099 return result; 100 } 101 102 @Override 103 @SuppressWarnings("unchecked") 104 public DependencySelector deriveChildSelector(DependencyCollectionContext context) { 105 requireNonNull(context, "context cannot be null"); 106 return new OptionalDependencySelector( 107 seed, 108 depth + 1, 109 applyFrom, 110 (Collection<String>) context.getSession().getData().get(IGNORED_KEYS), 111 (Collection<String>) context.getSession().getData().get(UNSELECTED_KEYS)); 112 } 113 114 @Override 115 public boolean equals(Object obj) { 116 if (this == obj) { 117 return true; 118 } else if (null == obj || !getClass().equals(obj.getClass())) { 119 return false; 120 } 121 122 OptionalDependencySelector that = (OptionalDependencySelector) obj; 123 return seed == that.seed && depth == that.depth && applyFrom == that.applyFrom; 124 } 125 126 @Override 127 public int hashCode() { 128 int hash = getClass().hashCode(); 129 hash = hash * 31 + seed; 130 hash = hash * 31 + depth; 131 hash = hash * 31 + applyFrom; 132 return hash; 133 } 134 135 @Override 136 public String toString() { 137 return String.format("%s(applied: %s)", this.getClass().getSimpleName(), depth >= applyFrom); 138 } 139}