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