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