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}