001package org.eclipse.aether.util.graph.version;
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;
024
025import org.eclipse.aether.RepositoryException;
026import org.eclipse.aether.collection.DependencyCollectionContext;
027import org.eclipse.aether.collection.VersionFilter;
028
029/**
030 * A version filter that combines multiple version filters into a chain where each filter gets invoked one after the
031 * other, thereby accumulating their filtering effects.
032 */
033public final class ChainedVersionFilter
034    implements VersionFilter
035{
036
037    private final VersionFilter[] filters;
038
039    private int hashCode;
040
041    /**
042     * Chains the specified version filters.
043     * 
044     * @param filter1 The first version filter, may be {@code null}.
045     * @param filter2 The second version filter, may be {@code null}.
046     * @return The chained version filter or {@code null} if both input filters are {@code null}.
047     */
048    public static VersionFilter newInstance( VersionFilter filter1, VersionFilter filter2 )
049    {
050        if ( filter1 == null )
051        {
052            return filter2;
053        }
054        if ( filter2 == null )
055        {
056            return filter1;
057        }
058        return new ChainedVersionFilter( new VersionFilter[] { filter1, filter2 } );
059    }
060
061    /**
062     * Chains the specified version filters.
063     * 
064     * @param filters The version filters to chain, must not be {@code null} or contain {@code null}.
065     * @return The chained version filter or {@code null} if the input array is empty.
066     */
067    public static VersionFilter newInstance( VersionFilter... filters )
068    {
069        if ( filters.length <= 1 )
070        {
071            if ( filters.length <= 0 )
072            {
073                return null;
074            }
075            return filters[0];
076        }
077        return new ChainedVersionFilter( filters.clone() );
078    }
079
080    /**
081     * Chains the specified version filters.
082     * 
083     * @param filters The version filters to chain, must not be {@code null} or contain {@code null}.
084     * @return The chained version filter or {@code null} if the input collection is empty.
085     */
086    public static VersionFilter newInstance( Collection<? extends VersionFilter> filters )
087    {
088        if ( filters.size() <= 1 )
089        {
090            if ( filters.isEmpty() )
091            {
092                return null;
093            }
094            return filters.iterator().next();
095        }
096        return new ChainedVersionFilter( filters.toArray( new VersionFilter[filters.size()] ) );
097    }
098
099    private ChainedVersionFilter( VersionFilter[] filters )
100    {
101        this.filters = filters;
102    }
103
104    public void filterVersions( VersionFilterContext context )
105        throws RepositoryException
106    {
107        for ( int i = 0, n = filters.length; i < n && context.getCount() > 0; i++ )
108        {
109            filters[i].filterVersions( context );
110        }
111    }
112
113    public VersionFilter deriveChildFilter( DependencyCollectionContext context )
114    {
115        VersionFilter[] children = null;
116        int removed = 0;
117        for ( int i = 0, n = filters.length; i < n; i++ )
118        {
119            VersionFilter child = filters[i].deriveChildFilter( context );
120            if ( children != null )
121            {
122                children[i - removed] = child;
123            }
124            else if ( child != filters[i] )
125            {
126                children = new VersionFilter[filters.length];
127                System.arraycopy( filters, 0, children, 0, i );
128                children[i - removed] = child;
129            }
130            if ( child == null )
131            {
132                removed++;
133            }
134        }
135        if ( children == null )
136        {
137            return this;
138        }
139        if ( removed > 0 )
140        {
141            int count = filters.length - removed;
142            if ( count <= 0 )
143            {
144                return null;
145            }
146            if ( count == 1 )
147            {
148                return children[0];
149            }
150            VersionFilter[] tmp = new VersionFilter[count];
151            System.arraycopy( children, 0, tmp, 0, count );
152            children = tmp;
153        }
154        return new ChainedVersionFilter( children );
155    }
156
157    @Override
158    public boolean equals( Object obj )
159    {
160        if ( this == obj )
161        {
162            return true;
163        }
164        else if ( null == obj || !getClass().equals( obj.getClass() ) )
165        {
166            return false;
167        }
168
169        ChainedVersionFilter that = (ChainedVersionFilter) obj;
170        return Arrays.equals( filters, that.filters );
171    }
172
173    @Override
174    public int hashCode()
175    {
176        if ( hashCode == 0 )
177        {
178            int hash = getClass().hashCode();
179            hash = hash * 31 + Arrays.hashCode( filters );
180            hashCode = hash;
181        }
182        return hashCode;
183    }
184
185}