001package org.eclipse.aether.util.graph.selector; 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.Arrays; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.Set; 028 029import org.eclipse.aether.collection.DependencyCollectionContext; 030import org.eclipse.aether.collection.DependencySelector; 031import org.eclipse.aether.graph.Dependency; 032 033import static java.util.Objects.requireNonNull; 034 035/** 036 * A dependency selector that combines zero or more other selectors using a logical {@code AND}. The resulting selector 037 * selects a given dependency if and only if all constituent selectors do so. 038 */ 039public final class AndDependencySelector 040 implements DependencySelector 041{ 042 043 private final Set<? extends DependencySelector> selectors; 044 045 private int hashCode; 046 047 /** 048 * Creates a new selector from the specified selectors. Prefer 049 * {@link #newInstance(DependencySelector, DependencySelector)} if any of the input selectors might be {@code null}. 050 * 051 * @param selectors The selectors to combine, may be {@code null} but must not contain {@code null} elements. 052 */ 053 public AndDependencySelector( DependencySelector... selectors ) 054 { 055 if ( selectors != null && selectors.length > 0 ) 056 { 057 this.selectors = new LinkedHashSet<>( Arrays.asList( selectors ) ); 058 } 059 else 060 { 061 this.selectors = Collections.emptySet(); 062 } 063 } 064 065 /** 066 * Creates a new selector from the specified selectors. 067 * 068 * @param selectors The selectors to combine, may be {@code null} but must not contain {@code null} elements. 069 */ 070 public AndDependencySelector( Collection<? extends DependencySelector> selectors ) 071 { 072 if ( selectors != null && !selectors.isEmpty() ) 073 { 074 this.selectors = new LinkedHashSet<>( selectors ); 075 } 076 else 077 { 078 this.selectors = Collections.emptySet(); 079 } 080 } 081 082 private AndDependencySelector( Set<DependencySelector> selectors ) 083 { 084 if ( selectors != null && !selectors.isEmpty() ) 085 { 086 this.selectors = selectors; 087 } 088 else 089 { 090 this.selectors = Collections.emptySet(); 091 } 092 } 093 094 /** 095 * Creates a new selector from the specified selectors. 096 * 097 * @param selector1 The first selector to combine, may be {@code null}. 098 * @param selector2 The second selector to combine, may be {@code null}. 099 * @return The combined selector or {@code null} if both selectors were {@code null}. 100 */ 101 public static DependencySelector newInstance( DependencySelector selector1, DependencySelector selector2 ) 102 { 103 if ( selector1 == null ) 104 { 105 return selector2; 106 } 107 else if ( selector2 == null || selector2.equals( selector1 ) ) 108 { 109 return selector1; 110 } 111 return new AndDependencySelector( selector1, selector2 ); 112 } 113 114 public boolean selectDependency( Dependency dependency ) 115 { 116 requireNonNull( dependency, "dependency cannot be null" ); 117 for ( DependencySelector selector : selectors ) 118 { 119 if ( !selector.selectDependency( dependency ) ) 120 { 121 return false; 122 } 123 } 124 return true; 125 } 126 127 public DependencySelector deriveChildSelector( DependencyCollectionContext context ) 128 { 129 requireNonNull( context, "context cannot be null" ); 130 int seen = 0; 131 Set<DependencySelector> childSelectors = null; 132 133 for ( DependencySelector selector : selectors ) 134 { 135 DependencySelector childSelector = selector.deriveChildSelector( context ); 136 if ( childSelectors != null ) 137 { 138 if ( childSelector != null ) 139 { 140 childSelectors.add( childSelector ); 141 } 142 } 143 else if ( selector != childSelector ) 144 { 145 childSelectors = new LinkedHashSet<>(); 146 if ( seen > 0 ) 147 { 148 for ( DependencySelector s : selectors ) 149 { 150 if ( childSelectors.size() >= seen ) 151 { 152 break; 153 } 154 childSelectors.add( s ); 155 } 156 } 157 if ( childSelector != null ) 158 { 159 childSelectors.add( childSelector ); 160 } 161 } 162 else 163 { 164 seen++; 165 } 166 } 167 168 if ( childSelectors == null ) 169 { 170 return this; 171 } 172 if ( childSelectors.size() <= 1 ) 173 { 174 if ( childSelectors.isEmpty() ) 175 { 176 return null; 177 } 178 return childSelectors.iterator().next(); 179 } 180 return new AndDependencySelector( childSelectors ); 181 } 182 183 @Override 184 public boolean equals( Object obj ) 185 { 186 if ( this == obj ) 187 { 188 return true; 189 } 190 else if ( null == obj || !getClass().equals( obj.getClass() ) ) 191 { 192 return false; 193 } 194 195 AndDependencySelector that = (AndDependencySelector) obj; 196 return selectors.equals( that.selectors ); 197 } 198 199 @Override 200 public int hashCode() 201 { 202 if ( hashCode == 0 ) 203 { 204 int hash = 17; 205 hash = hash * 31 + selectors.hashCode(); 206 hashCode = hash; 207 } 208 return hashCode; 209 } 210 211 @Override 212 public String toString() 213 { 214 StringBuilder builder = new StringBuilder().append( this.getClass().getSimpleName() ).append( '(' ); 215 Iterator<? extends DependencySelector> iterator = this.selectors.iterator(); 216 while ( iterator.hasNext() ) 217 { 218 final DependencySelector selector = iterator.next(); 219 builder.append( selector.toString() ); 220 if ( iterator.hasNext() ) // not last 221 { 222 builder.append( " && " ); 223 } 224 } 225 return builder.append( ')' ).toString(); 226 } 227 228}