001package org.apache.archiva.indexer.search;
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 org.apache.archiva.admin.model.RepositoryAdminException;
023import org.apache.archiva.admin.model.beans.ManagedRepository;
024import org.apache.archiva.admin.model.beans.ProxyConnector;
025import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
026import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin;
027import org.apache.archiva.common.plexusbridge.MavenIndexerUtils;
028import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
029import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
030import org.apache.archiva.indexer.util.SearchUtil;
031import org.apache.commons.lang.StringUtils;
032import org.apache.lucene.queryParser.ParseException;
033import org.apache.lucene.search.BooleanClause;
034import org.apache.lucene.search.BooleanClause.Occur;
035import org.apache.lucene.search.BooleanQuery;
036import org.apache.lucene.search.Query;
037import org.apache.maven.index.ArtifactInfo;
038import org.apache.maven.index.FlatSearchRequest;
039import org.apache.maven.index.FlatSearchResponse;
040import org.apache.maven.index.MAVEN;
041import org.apache.maven.index.NexusIndexer;
042import org.apache.maven.index.OSGI;
043import org.apache.maven.index.QueryCreator;
044import org.apache.maven.index.SearchType;
045import org.apache.maven.index.context.IndexCreator;
046import org.apache.maven.index.context.IndexingContext;
047import org.apache.maven.index.expr.SearchExpression;
048import org.apache.maven.index.expr.SearchTyped;
049import org.apache.maven.index.expr.SourcedSearchExpression;
050import org.apache.maven.index.expr.UserInputSearchExpression;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053import org.springframework.stereotype.Service;
054
055import javax.inject.Inject;
056import java.io.IOException;
057import java.util.ArrayList;
058import java.util.Collection;
059import java.util.Collections;
060import java.util.HashSet;
061import java.util.List;
062import java.util.Map;
063import java.util.Set;
064
065/**
066 * RepositorySearch implementation which uses the Maven Indexer for searching.
067 */
068@Service( "repositorySearch#maven" )
069public class MavenRepositorySearch
070    implements RepositorySearch
071{
072    private Logger log = LoggerFactory.getLogger( getClass() );
073
074    private NexusIndexer indexer;
075
076    private QueryCreator queryCreator;
077
078    private ManagedRepositoryAdmin managedRepositoryAdmin;
079
080    private ProxyConnectorAdmin proxyConnectorAdmin;
081
082    private MavenIndexerUtils mavenIndexerUtils;
083
084    protected MavenRepositorySearch()
085    {
086        // for test purpose
087    }
088
089    @Inject
090    public MavenRepositorySearch( PlexusSisuBridge plexusSisuBridge, ManagedRepositoryAdmin managedRepositoryAdmin,
091                                  MavenIndexerUtils mavenIndexerUtils, ProxyConnectorAdmin proxyConnectorAdmin )
092        throws PlexusSisuBridgeException
093    {
094        this.indexer = plexusSisuBridge.lookup( NexusIndexer.class );
095        this.queryCreator = plexusSisuBridge.lookup( QueryCreator.class );
096        this.managedRepositoryAdmin = managedRepositoryAdmin;
097        this.mavenIndexerUtils = mavenIndexerUtils;
098        this.proxyConnectorAdmin = proxyConnectorAdmin;
099    }
100
101    /**
102     * @see RepositorySearch#search(String, List, String, SearchResultLimits, List)
103     */
104    @Override
105    public SearchResults search( String principal, List<String> selectedRepos, String term, SearchResultLimits limits,
106                                 List<String> previousSearchTerms )
107        throws RepositorySearchException
108    {
109        List<String> indexingContextIds = addIndexingContexts( selectedRepos );
110
111        // since upgrade to nexus 2.0.0, query has changed from g:[QUERIED TERM]* to g:*[QUERIED TERM]*
112        //      resulting to more wildcard searches so we need to increase max clause count
113        BooleanQuery.setMaxClauseCount( Integer.MAX_VALUE );
114        BooleanQuery q = new BooleanQuery();
115
116        if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
117        {
118            constructQuery( term, q );
119        }
120        else
121        {
122            for ( String previousTerm : previousSearchTerms )
123            {
124                BooleanQuery iQuery = new BooleanQuery();
125                constructQuery( previousTerm, iQuery );
126
127                q.add( iQuery, Occur.MUST );
128            }
129
130            BooleanQuery iQuery = new BooleanQuery();
131            constructQuery( term, iQuery );
132            q.add( iQuery, Occur.MUST );
133        }
134
135        // we retun only artifacts without classifier in quick search, olamy cannot find a way to say with this field empty
136        // FIXME  cannot find a way currently to setup this in constructQuery !!!
137        return search( limits, q, indexingContextIds, NoClassifierArtifactInfoFilter.LIST, selectedRepos, true );
138
139    }
140
141    /**
142     * @see RepositorySearch#search(String, SearchFields, SearchResultLimits)
143     */
144    @Override
145    public SearchResults search( String principal, SearchFields searchFields, SearchResultLimits limits )
146        throws RepositorySearchException
147    {
148        if ( searchFields.getRepositories() == null )
149        {
150            throw new RepositorySearchException( "Repositories cannot be null." );
151        }
152
153        List<String> indexingContextIds = addIndexingContexts( searchFields.getRepositories() );
154
155        // if no index found in the specified ones return an empty search result instead of doing a search on all index
156        // olamy: IMHO doesn't make sense
157        if ( !searchFields.getRepositories().isEmpty() && ( indexingContextIds == null
158            || indexingContextIds.isEmpty() ) )
159        {
160            return new SearchResults();
161        }
162
163        BooleanQuery q = new BooleanQuery();
164        if ( StringUtils.isNotBlank( searchFields.getGroupId() ) )
165        {
166            q.add( indexer.constructQuery( MAVEN.GROUP_ID, searchFields.isExactSearch()
167                                               ? new SourcedSearchExpression( searchFields.getGroupId() )
168                                               : new UserInputSearchExpression( searchFields.getGroupId() )
169                   ), Occur.MUST
170            );
171        }
172
173        if ( StringUtils.isNotBlank( searchFields.getArtifactId() ) )
174        {
175            q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID,
176                                           searchFields.isExactSearch()
177                                               ? new SourcedSearchExpression( searchFields.getArtifactId() )
178                                               : new UserInputSearchExpression( searchFields.getArtifactId() )
179                   ), Occur.MUST
180            );
181        }
182
183        if ( StringUtils.isNotBlank( searchFields.getVersion() ) )
184        {
185            q.add( indexer.constructQuery( MAVEN.VERSION, searchFields.isExactSearch() ? new SourcedSearchExpression(
186                searchFields.getVersion() ) : new SourcedSearchExpression( searchFields.getVersion() ) ), Occur.MUST );
187        }
188
189        if ( StringUtils.isNotBlank( searchFields.getPackaging() ) )
190        {
191            q.add( indexer.constructQuery( MAVEN.PACKAGING, searchFields.isExactSearch() ? new SourcedSearchExpression(
192                       searchFields.getPackaging() ) : new UserInputSearchExpression( searchFields.getPackaging() ) ),
193                   Occur.MUST
194            );
195        }
196
197        if ( StringUtils.isNotBlank( searchFields.getClassName() ) )
198        {
199            q.add( indexer.constructQuery( MAVEN.CLASSNAMES,
200                                           new UserInputSearchExpression( searchFields.getClassName() ) ), Occur.MUST );
201        }
202
203        if ( StringUtils.isNotBlank( searchFields.getBundleSymbolicName() ) )
204        {
205            q.add( indexer.constructQuery( OSGI.SYMBOLIC_NAME,
206                                           new UserInputSearchExpression( searchFields.getBundleSymbolicName() ) ),
207                   Occur.MUST
208            );
209        }
210
211        if ( StringUtils.isNotBlank( searchFields.getBundleVersion() ) )
212        {
213            q.add( indexer.constructQuery( OSGI.VERSION,
214                                           new UserInputSearchExpression( searchFields.getBundleVersion() ) ),
215                   Occur.MUST
216            );
217        }
218
219        if ( StringUtils.isNotBlank( searchFields.getBundleExportPackage() ) )
220        {
221            q.add( indexer.constructQuery( OSGI.EXPORT_PACKAGE,
222                                           new UserInputSearchExpression( searchFields.getBundleExportPackage() ) ),
223                   Occur.MUST
224            );
225        }
226
227        if ( StringUtils.isNotBlank( searchFields.getBundleExportService() ) )
228        {
229            q.add( indexer.constructQuery( OSGI.EXPORT_SERVICE,
230                                           new UserInputSearchExpression( searchFields.getBundleExportService() ) ),
231                   Occur.MUST
232            );
233        }
234
235        if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
236        {
237            q.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
238                                           new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
239                   Occur.MUST
240            );
241        }
242
243        if ( StringUtils.isNotBlank( searchFields.getBundleName() ) )
244        {
245            q.add( indexer.constructQuery( OSGI.NAME, new UserInputSearchExpression( searchFields.getBundleName() ) ),
246                   Occur.MUST );
247        }
248
249        if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
250        {
251            q.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
252                                           new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
253                   Occur.MUST
254            );
255        }
256
257        if ( StringUtils.isNotBlank( searchFields.getBundleRequireBundle() ) )
258        {
259            q.add( indexer.constructQuery( OSGI.REQUIRE_BUNDLE,
260                                           new UserInputSearchExpression( searchFields.getBundleRequireBundle() ) ),
261                   Occur.MUST
262            );
263        }
264
265        if ( StringUtils.isNotBlank( searchFields.getClassifier() ) )
266        {
267            q.add( indexer.constructQuery( MAVEN.CLASSIFIER, searchFields.isExactSearch() ? new SourcedSearchExpression(
268                       searchFields.getClassifier() ) : new UserInputSearchExpression( searchFields.getClassifier() ) ),
269                   Occur.MUST
270            );
271        }
272        else if ( searchFields.isExactSearch() )
273        {
274            //TODO improvement in case of exact search and no classifier we must query for classifier with null value
275            // currently it's done in DefaultSearchService with some filtering
276        }
277
278        if ( q.getClauses() == null || q.getClauses().length <= 0 )
279        {
280            throw new RepositorySearchException( "No search fields set." );
281        }
282
283        return search( limits, q, indexingContextIds, Collections.<ArtifactInfoFilter>emptyList(),
284                       searchFields.getRepositories(), searchFields.isIncludePomArtifacts() );
285    }
286
287    private static class NullSearch implements SearchTyped, SearchExpression
288    {
289        private static final NullSearch INSTANCE = new NullSearch();
290
291        @Override
292        public String getStringValue()
293        {
294            return "[[NULL_VALUE]]";
295        }
296
297        @Override
298        public SearchType getSearchType()
299        {
300            return SearchType.EXACT;
301        }
302    }
303
304    private SearchResults search( SearchResultLimits limits, BooleanQuery q, List<String> indexingContextIds,
305                                  List<? extends ArtifactInfoFilter> filters, List<String> selectedRepos,
306                                  boolean includePoms )
307        throws RepositorySearchException
308    {
309
310        try
311        {
312            FlatSearchRequest request = new FlatSearchRequest( q );
313
314            request.setContexts( getIndexingContexts( indexingContextIds ) );
315            if ( limits != null )
316            {
317                // we apply limits only when first page asked
318                if ( limits.getSelectedPage() == 0 )
319                {
320                    request.setCount( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
321                }
322            }
323
324            FlatSearchResponse response = indexer.searchFlat( request );
325
326            if ( response == null || response.getTotalHits() == 0 )
327            {
328                SearchResults results = new SearchResults();
329                results.setLimits( limits );
330                return results;
331            }
332
333            return convertToSearchResults( response, limits, filters, selectedRepos, includePoms );
334        }
335        catch ( IOException e )
336        {
337            throw new RepositorySearchException( e.getMessage(), e );
338        }
339        catch ( RepositoryAdminException e )
340        {
341            throw new RepositorySearchException( e.getMessage(), e );
342        }
343
344    }
345
346    private List<IndexingContext> getIndexingContexts( List<String> ids )
347    {
348        List<IndexingContext> contexts = new ArrayList<>( ids.size() );
349
350        for ( String id : ids )
351        {
352            IndexingContext context = indexer.getIndexingContexts().get( id );
353            if ( context != null )
354            {
355                contexts.add( context );
356            }
357            else
358            {
359                log.warn( "context with id {} not exists", id );
360            }
361        }
362
363        return contexts;
364    }
365
366    private void constructQuery( String term, BooleanQuery q )
367    {
368        q.add( indexer.constructQuery( MAVEN.GROUP_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
369        q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
370        q.add( indexer.constructQuery( MAVEN.VERSION, new UserInputSearchExpression( term ) ), Occur.SHOULD );
371        q.add( indexer.constructQuery( MAVEN.PACKAGING, new UserInputSearchExpression( term ) ), Occur.SHOULD );
372        q.add( indexer.constructQuery( MAVEN.CLASSNAMES, new UserInputSearchExpression( term ) ), Occur.SHOULD );
373
374        //Query query =
375        //    new WildcardQuery( new Term( MAVEN.CLASSNAMES.getFieldName(), "*" ) );
376        //q.add( query, Occur.MUST_NOT );
377        // olamy IMHO we could set this option as at least one must match
378        //q.setMinimumNumberShouldMatch( 1 );
379    }
380
381
382    /**
383     * @param selectedRepos
384     * @return indexing contextId used
385     */
386    private List<String> addIndexingContexts( List<String> selectedRepos )
387    {
388        Set<String> indexingContextIds = new HashSet<>();
389        for ( String repo : selectedRepos )
390        {
391            try
392            {
393                ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repo );
394
395                if ( repoConfig != null )
396                {
397
398                    IndexingContext context = managedRepositoryAdmin.createIndexContext( repoConfig );
399                    if ( context.isSearchable() )
400                    {
401                        indexingContextIds.addAll( getRemoteIndexingContextIds( repo ) );
402                        indexingContextIds.add( context.getId() );
403                    }
404                    else
405                    {
406                        log.warn( "indexingContext with id {} not searchable", repoConfig.getId() );
407                    }
408
409                }
410                else
411                {
412                    log.warn( "Repository '{}' not found in configuration.", repo );
413                }
414            }
415            catch ( RepositoryAdminException e )
416            {
417                log.warn( "RepositoryAdminException occured while accessing index of repository '{}' : {}", repo,
418                          e.getMessage() );
419                continue;
420            }
421        }
422
423        return new ArrayList<>( indexingContextIds );
424    }
425
426
427    @Override
428    public Set<String> getRemoteIndexingContextIds( String managedRepoId )
429        throws RepositoryAdminException
430    {
431        Set<String> ids = new HashSet<>();
432
433        List<ProxyConnector> proxyConnectors = proxyConnectorAdmin.getProxyConnectorAsMap().get( managedRepoId );
434
435        if ( proxyConnectors == null || proxyConnectors.isEmpty() )
436        {
437            return ids;
438        }
439
440        for ( ProxyConnector proxyConnector : proxyConnectors )
441        {
442            String remoteId = "remote-" + proxyConnector.getTargetRepoId();
443            IndexingContext context = indexer.getIndexingContexts().get( remoteId );
444            if ( context != null && context.isSearchable() )
445            {
446                ids.add( remoteId );
447            }
448        }
449
450        return ids;
451    }
452
453    @Override
454    public Collection<String> getAllGroupIds( String principal, List<String> selectedRepos )
455        throws RepositorySearchException
456    {
457        List<IndexingContext> indexContexts = getIndexingContexts( selectedRepos );
458
459        if ( indexContexts == null || indexContexts.isEmpty() )
460        {
461            return Collections.emptyList();
462        }
463
464        try
465        {
466            Set<String> allGroupIds = new HashSet<>();
467            for ( IndexingContext indexingContext : indexContexts )
468            {
469                allGroupIds.addAll( indexingContext.getAllGroups() );
470            }
471            return allGroupIds;
472        }
473        catch ( IOException e )
474        {
475            throw new RepositorySearchException( e.getMessage(), e );
476        }
477
478    }
479
480
481    protected List<? extends IndexCreator> getAllIndexCreators()
482    {
483        return mavenIndexerUtils.getAllIndexCreators();
484    }
485
486
487    private SearchResults convertToSearchResults( FlatSearchResponse response, SearchResultLimits limits,
488                                                  List<? extends ArtifactInfoFilter> artifactInfoFilters,
489                                                  List<String> selectedRepos, boolean includePoms )
490        throws RepositoryAdminException
491    {
492        SearchResults results = new SearchResults();
493        Set<ArtifactInfo> artifactInfos = response.getResults();
494
495        for ( ArtifactInfo artifactInfo : artifactInfos )
496        {
497            if ( StringUtils.equalsIgnoreCase( "pom", artifactInfo.fextension ) && !includePoms )
498            {
499                continue;
500            }
501            String id = SearchUtil.getHitId( artifactInfo.groupId, artifactInfo.artifactId, artifactInfo.classifier,
502                                             artifactInfo.packaging );
503            Map<String, SearchResultHit> hitsMap = results.getHitsMap();
504
505            if ( !applyArtifactInfoFilters( artifactInfo, artifactInfoFilters, hitsMap ) )
506            {
507                continue;
508            }
509
510            SearchResultHit hit = hitsMap.get( id );
511            if ( hit != null )
512            {
513                if ( !hit.getVersions().contains( artifactInfo.version ) )
514                {
515                    hit.addVersion( artifactInfo.version );
516                }
517            }
518            else
519            {
520                hit = new SearchResultHit();
521                hit.setArtifactId( artifactInfo.artifactId );
522                hit.setGroupId( artifactInfo.groupId );
523                hit.setRepositoryId( artifactInfo.repository );
524                hit.addVersion( artifactInfo.version );
525                hit.setBundleExportPackage( artifactInfo.bundleExportPackage );
526                hit.setBundleExportService( artifactInfo.bundleExportService );
527                hit.setBundleSymbolicName( artifactInfo.bundleSymbolicName );
528                hit.setBundleVersion( artifactInfo.bundleVersion );
529                hit.setBundleDescription( artifactInfo.bundleDescription );
530                hit.setBundleDocUrl( artifactInfo.bundleDocUrl );
531                hit.setBundleRequireBundle( artifactInfo.bundleRequireBundle );
532                hit.setBundleImportPackage( artifactInfo.bundleImportPackage );
533                hit.setBundleLicense( artifactInfo.bundleLicense );
534                hit.setBundleName( artifactInfo.bundleName );
535                hit.setContext( artifactInfo.context );
536                hit.setGoals( artifactInfo.goals );
537                hit.setPrefix( artifactInfo.prefix );
538                hit.setPackaging( artifactInfo.packaging );
539                hit.setClassifier( artifactInfo.classifier );
540                hit.setFileExtension( artifactInfo.fextension );
541                hit.setUrl( getBaseUrl( artifactInfo, selectedRepos ) );
542            }
543
544            results.addHit( id, hit );
545        }
546
547        results.setTotalHits( response.getTotalHitsCount() );
548        results.setTotalHitsMapSize( results.getHitsMap().values().size() );
549        results.setReturnedHitsCount( response.getReturnedHitsCount() );
550        results.setLimits( limits );
551
552        if ( limits == null || limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
553        {
554            return results;
555        }
556        else
557        {
558            return paginate( results );
559        }
560    }
561
562    /**
563     * calculate baseUrl without the context and base Archiva Url
564     *
565     * @param artifactInfo
566     * @return
567     */
568    protected String getBaseUrl( ArtifactInfo artifactInfo, List<String> selectedRepos )
569        throws RepositoryAdminException
570    {
571        StringBuilder sb = new StringBuilder();
572        if ( StringUtils.startsWith( artifactInfo.context, "remote-" ) )
573        {
574            // it's a remote index result we search a managed which proxying this remote and on which
575            // current user has read karma
576            String managedRepoId =
577                getManagedRepoId( StringUtils.substringAfter( artifactInfo.context, "remote-" ), selectedRepos );
578            if ( managedRepoId != null )
579            {
580                sb.append( '/' ).append( managedRepoId );
581                artifactInfo.context = managedRepoId;
582            }
583        }
584        else
585        {
586            sb.append( '/' ).append( artifactInfo.context );
587        }
588
589        sb.append( '/' ).append( StringUtils.replaceChars( artifactInfo.groupId, '.', '/' ) );
590        sb.append( '/' ).append( artifactInfo.artifactId );
591        sb.append( '/' ).append( artifactInfo.version );
592        sb.append( '/' ).append( artifactInfo.artifactId );
593        sb.append( '-' ).append( artifactInfo.version );
594        if ( StringUtils.isNotBlank( artifactInfo.classifier ) )
595        {
596            sb.append( '-' ).append( artifactInfo.classifier );
597        }
598        // maven-plugin packaging is a jar
599        if ( StringUtils.equals( "maven-plugin", artifactInfo.packaging ) )
600        {
601            sb.append( "jar" );
602        }
603        else
604        {
605            sb.append( '.' ).append( artifactInfo.packaging );
606        }
607
608        return sb.toString();
609    }
610
611    /**
612     * return a managed repo for a remote result
613     *
614     * @param remoteRepo
615     * @param selectedRepos
616     * @return
617     * @throws RepositoryAdminException
618     */
619    private String getManagedRepoId( String remoteRepo, List<String> selectedRepos )
620        throws RepositoryAdminException
621    {
622        Map<String, List<ProxyConnector>> proxyConnectorMap = proxyConnectorAdmin.getProxyConnectorAsMap();
623        if ( proxyConnectorMap == null || proxyConnectorMap.isEmpty() )
624        {
625            return null;
626        }
627        if ( selectedRepos != null && !selectedRepos.isEmpty() )
628        {
629            for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
630            {
631                if ( selectedRepos.contains( entry.getKey() ) )
632                {
633                    for ( ProxyConnector proxyConnector : entry.getValue() )
634                    {
635                        if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
636                        {
637                            return proxyConnector.getSourceRepoId();
638                        }
639                    }
640                }
641            }
642        }
643
644        // we don't find in search selected repos so return the first one
645        for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
646        {
647
648            for ( ProxyConnector proxyConnector : entry.getValue() )
649            {
650                if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
651                {
652                    return proxyConnector.getSourceRepoId();
653                }
654            }
655
656        }
657        return null;
658    }
659
660    private boolean applyArtifactInfoFilters( ArtifactInfo artifactInfo,
661                                              List<? extends ArtifactInfoFilter> artifactInfoFilters,
662                                              Map<String, SearchResultHit> currentResult )
663    {
664        if ( artifactInfoFilters == null || artifactInfoFilters.isEmpty() )
665        {
666            return true;
667        }
668
669        for ( ArtifactInfoFilter filter : artifactInfoFilters )
670        {
671            if ( !filter.addArtifactInResult( artifactInfo, currentResult ) )
672            {
673                return false;
674            }
675        }
676        return true;
677    }
678
679    protected SearchResults paginate( SearchResults results )
680    {
681        SearchResultLimits limits = results.getLimits();
682        SearchResults paginated = new SearchResults();
683
684        // ( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
685
686        int fetchCount = limits.getPageSize();
687        int offset = ( limits.getSelectedPage() * limits.getPageSize() );
688
689        if ( fetchCount > results.getTotalHits() )
690        {
691            fetchCount = results.getTotalHits();
692        }
693
694        // Goto offset.
695        if ( offset < results.getTotalHits() )
696        {
697            // only process if the offset is within the hit count.
698            for ( int i = 0; i < fetchCount; i++ )
699            {
700                // Stop fetching if we are past the total # of available hits.
701                if ( offset + i >= results.getHits().size() )
702                {
703                    break;
704                }
705
706                SearchResultHit hit = results.getHits().get( ( offset + i ) );
707                if ( hit != null )
708                {
709                    String id = SearchUtil.getHitId( hit.getGroupId(), hit.getArtifactId(), hit.getClassifier(),
710                                                     hit.getPackaging() );
711                    paginated.addHit( id, hit );
712                }
713                else
714                {
715                    break;
716                }
717            }
718        }
719        paginated.setTotalHits( results.getTotalHits() );
720        paginated.setReturnedHitsCount( paginated.getHits().size() );
721        paginated.setTotalHitsMapSize( results.getTotalHitsMapSize() );
722        paginated.setLimits( limits );
723
724        return paginated;
725    }
726
727
728}