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}