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