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